Implement folder support for VDS.

This commit is contained in:
Lukas Brübach 2025-02-02 06:02:43 +01:00
parent 95cea0f191
commit 0ec9c20d69
10 changed files with 290 additions and 96 deletions

View file

@ -188,6 +188,7 @@ set(cockatrice_SOURCES
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp

View file

@ -39,7 +39,7 @@ void DeckPreviewTagAdditionWidget::mousePressEvent(QMouseEvent *event)
emit tagClicked();
}
QWidget::mousePressEvent(event);
QStringList knownTags = parent->parent->parent->gatherAllTagsFromFlowWidget();
QStringList knownTags = parent->parent->parent->tagFilterWidget->getAllKnownTags();
QStringList activeTags = parent->deckLoader->getTags();
bool canAddTags = true;

View file

@ -17,6 +17,8 @@ DeckPreviewWidget::DeckPreviewWidget(VisualDeckStorageWidget *_parent, const QSt
deckLoader = new DeckLoader();
connect(deckLoader, &DeckLoader::loadFinished, this, &DeckPreviewWidget::initializeUi);
connect(deckLoader, &DeckLoader::loadFinished, parent->tagFilterWidget,
&VisualDeckStorageTagFilterWidget::refreshTags);
deckLoader->loadFromFileAsync(filePath, DeckLoader::getFormatFromName(filePath), false);
bannerCardDisplayWidget = new DeckPreviewCardPictureWidget(this);
@ -26,6 +28,8 @@ DeckPreviewWidget::DeckPreviewWidget(VisualDeckStorageWidget *_parent, const QSt
connect(bannerCardDisplayWidget, &DeckPreviewCardPictureWidget::imageDoubleClicked, this,
&DeckPreviewWidget::imageDoubleClickedEvent);
connect(parent, &VisualDeckStorageWidget::tagFilterUpdated, this, &DeckPreviewWidget::checkVisibility);
layout->addWidget(bannerCardDisplayWidget);
}
@ -52,6 +56,16 @@ void DeckPreviewWidget::initializeUi(const bool deckLoadSuccess)
layout->addWidget(deckTagsDisplayWidget);
}
void DeckPreviewWidget::checkVisibility()
{
if (filteredBySearch || filteredByColor || filteredByTags) {
setVisible(false);
} else {
setVisible(true);
}
emit visibilityUpdated();
}
QString DeckPreviewWidget::getColorIdentity()
{
QStringList cardList = deckLoader->getCardList();

View file

@ -26,10 +26,14 @@ public:
DeckPreviewCardPictureWidget *bannerCardDisplayWidget;
DeckPreviewColorIdentityWidget *colorIdentityWidget;
DeckPreviewDeckTagsDisplayWidget *deckTagsDisplayWidget;
bool filteredBySearch = false;
bool filteredByColor = false;
bool filteredByTags = false;
signals:
void deckPreviewClicked(QMouseEvent *event, DeckPreviewWidget *instance);
void deckPreviewDoubleClicked(QMouseEvent *event, DeckPreviewWidget *instance);
void visibilityUpdated();
public slots:
void setFilePath(const QString &filePath);
@ -37,6 +41,7 @@ public slots:
void imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
void imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
void initializeUi(bool deckLoadSuccess);
void checkVisibility();
};
#endif // DECK_PREVIEW_WIDGET_H

View file

@ -0,0 +1,156 @@
#include "visual_deck_storage_folder_display_widget.h"
#include "../../../../settings/cache_settings.h"
#include "deck_preview/deck_preview_widget.h"
#include <QDirIterator>
VisualDeckStorageFolderDisplayWidget::VisualDeckStorageFolderDisplayWidget(
QWidget *parent,
VisualDeckStorageWidget *_visualDeckStorageWidget,
QString _filePath)
: QWidget(parent), visualDeckStorageWidget(_visualDeckStorageWidget), filePath(_filePath)
{
layout = new QVBoxLayout(this);
setLayout(layout);
QString bannerText = "Deck Storage";
QString deckPath = SettingsCache::instance().getDeckPath();
if (filePath != deckPath) {
QString relativePath = filePath;
if (filePath.startsWith(deckPath)) {
relativePath = filePath.mid(deckPath.length()); // Remove the deckPath prefix
if (relativePath.startsWith('/')) {
relativePath.remove(0, 1); // Remove leading '/' if it exists
}
}
bannerText = relativePath;
}
header = new BannerWidget(this, bannerText);
layout->addWidget(header);
container = new QWidget(this);
containerLayout = new QVBoxLayout(container);
container->setLayout(containerLayout);
header->setBuddy(container);
layout->addWidget(container);
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff);
containerLayout->addWidget(flowWidget);
createWidgetsForFiles();
createWidgetsForFolders();
}
void VisualDeckStorageFolderDisplayWidget::createWidgetsForFiles()
{
QList<DeckPreviewWidget *> allDecks;
for (const QString &file : getAllFiles()) {
auto *display = new DeckPreviewWidget(visualDeckStorageWidget, file);
connect(display, &DeckPreviewWidget::deckPreviewClicked, visualDeckStorageWidget,
&VisualDeckStorageWidget::deckPreviewClickedEvent);
connect(display, &DeckPreviewWidget::deckPreviewDoubleClicked, visualDeckStorageWidget,
&VisualDeckStorageWidget::deckPreviewDoubleClickedEvent);
connect(visualDeckStorageWidget->cardSizeWidget->getSlider(), &QSlider::valueChanged, display->bannerCardDisplayWidget,
&CardInfoPictureWidget::setScaleFactor);
connect(display, &DeckPreviewWidget::visibilityUpdated, this, &VisualDeckStorageFolderDisplayWidget::updateVisibility);
display->bannerCardDisplayWidget->setScaleFactor(visualDeckStorageWidget->cardSizeWidget->getSlider()->value());
allDecks.append(display);
}
/*auto filteredByColorIdentity =
deckPreviewColorIdentityFilterWidget->filterWidgets(sortWidget->filterFiles(allDecks));
auto filteredByTags = tagFilterWidget->filterDecksBySelectedTags(filteredByColorIdentity);
auto filteredFiles = searchWidget->filterFiles(filteredByTags, searchWidget->getSearchText());
*/
flowWidget->clearLayout(); // Clear existing widgets in the flow layout
for (DeckPreviewWidget *deck : allDecks) {
flowWidget->addWidget(deck);
}
//emit bannerCardsRefreshed();
}
void VisualDeckStorageFolderDisplayWidget::updateVisibility()
{
bool atLeastOneWidgetVisible = false;
if (flowWidget) {
// Iterate through all DeckPreviewWidgets
for (DeckPreviewWidget *display : flowWidget->findChildren<DeckPreviewWidget *>()) {
// Get tags from each DeckPreviewWidget
if (display->isVisible()) {
atLeastOneWidgetVisible = true;
}
}
}
if (atLeastOneWidgetVisible) {
//setVisible(true);
} else {
//setVisible(false);
}
}
void VisualDeckStorageFolderDisplayWidget::createWidgetsForFolders()
{
for (const QString &dir : getAllSubFolders()) {
auto *display = new VisualDeckStorageFolderDisplayWidget(this, visualDeckStorageWidget, dir);
containerLayout->addWidget(display);
}
}
QStringList VisualDeckStorageFolderDisplayWidget::gatherAllTagsFromFlowWidget() const
{
QStringList allTags;
if (flowWidget) {
// Iterate through all DeckPreviewWidgets
for (DeckPreviewWidget *display : flowWidget->findChildren<DeckPreviewWidget *>()) {
// Get tags from each DeckPreviewWidget
QStringList tags = display->deckLoader->getTags();
// Add tags to the list while avoiding duplicates
allTags.append(tags);
}
}
// Remove duplicates by calling 'removeDuplicates'
allTags.removeDuplicates();
return allTags;
}
QStringList VisualDeckStorageFolderDisplayWidget::getAllFiles() const
{
QStringList allFiles;
// QDirIterator with QDir::Files and QDir::NoSymLinks ensures only files are listed (no directories or symlinks)
QDirIterator it(filePath, QDir::Files | QDir::NoSymLinks);
while (it.hasNext()) {
allFiles << it.next(); // Add each file path to the list
}
return allFiles;
}
QStringList VisualDeckStorageFolderDisplayWidget::getAllSubFolders() const
{
QStringList allFolders;
// QDirIterator with QDir::Files and QDir::NoSymLinks ensures only files are listed (no directories or symlinks)
QDirIterator it(filePath, QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
while (it.hasNext()) {
allFolders << it.next(); // Add each file path to the list
}
return allFolders;
}

View file

@ -0,0 +1,37 @@
#ifndef VISUAL_DECK_STORAGE_FOLDER_DISPLAY_WIDGET_H
#define VISUAL_DECK_STORAGE_FOLDER_DISPLAY_WIDGET_H
#include "../general/display/banner_widget.h"
#include "../general/layout_containers/flow_widget.h"
#include "visual_deck_storage_widget.h"
#include <QVBoxLayout>
#include <QWidget>
class VisualDeckStorageFolderDisplayWidget : public QWidget
{
Q_OBJECT
public:
VisualDeckStorageFolderDisplayWidget(QWidget *parent,
VisualDeckStorageWidget *_visualDeckStorageWidget,
QString _filePath);
void createWidgetsForFiles();
void createWidgetsForFolders();
QStringList gatherAllTagsFromFlowWidget() const;
[[nodiscard]] QStringList getAllFiles() const;
[[nodiscard]] QStringList getAllSubFolders() const;
public slots:
void updateVisibility();
private:
QVBoxLayout *layout;
VisualDeckStorageWidget *visualDeckStorageWidget;
QString filePath;
BannerWidget *header;
QWidget *container;
QVBoxLayout *containerLayout;
FlowWidget *flowWidget;
};
#endif // VISUAL_DECK_STORAGE_FOLDER_DISPLAY_WIDGET_H

View file

@ -25,8 +25,7 @@ VisualDeckStorageTagFilterWidget::VisualDeckStorageTagFilterWidget(VisualDeckSto
layout->addWidget(flowWidget);
}
QList<DeckPreviewWidget *>
VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const
void VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const
{
// Collect selected tags from DeckPreviewTagDisplayWidget
QStringList selectedTags;
@ -36,9 +35,12 @@ VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QList<DeckPrev
}
}
// If no tags are selected, return all decks
// If no tags are selected, set all decks as visible
if (selectedTags.isEmpty()) {
return deckPreviews;
for (DeckPreviewWidget *deckPreview : deckPreviews) {
deckPreview->filteredByTags = false;
}
return;
}
// Filter DeckPreviewWidgets that contain all of the selected tags
@ -50,12 +52,15 @@ VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QList<DeckPrev
bool allTagsPresent = std::all_of(selectedTags.begin(), selectedTags.end(),
[&deckTags](const QString &tag) { return deckTags.contains(tag); });
if (allTagsPresent) {
filteredDecks.append(deckPreview);
}
deckPreview->filteredByTags = !allTagsPresent;
}
}
return filteredDecks;
void VisualDeckStorageTagFilterWidget::refreshTags()
{
QStringList allTags = gatherAllTags();
removeTagsNotInList(gatherAllTags());
addTagsIfNotPresent(gatherAllTags());
}
void VisualDeckStorageTagFilterWidget::removeTagsNotInList(const QStringList &tags)
@ -93,8 +98,37 @@ void VisualDeckStorageTagFilterWidget::addTagIfNotPresent(const QString &tag)
if (!tagExists) {
auto *newTagWidget = new DeckPreviewTagDisplayWidget(this, tag);
connect(newTagWidget, &DeckPreviewTagDisplayWidget::tagClicked, parent,
&VisualDeckStorageWidget::refreshBannerCards);
&VisualDeckStorageWidget::updateTagFilter);
connect(newTagWidget, &DeckPreviewTagDisplayWidget::tagClicked, this,
&VisualDeckStorageTagFilterWidget::refreshTags);
auto *flowWidget = findChild<FlowWidget *>();
flowWidget->addWidget(newTagWidget);
}
}
QStringList VisualDeckStorageTagFilterWidget::gatherAllTags()
{
QStringList allTags;
QList<DeckPreviewWidget *> deckWidgets = parent->findChildren<DeckPreviewWidget *>();
for (DeckPreviewWidget *widget : deckWidgets) {
if (widget->isVisible()) {
allTags << widget->deckLoader->getTags();
}
}
return allTags;
}
QStringList VisualDeckStorageTagFilterWidget::getAllKnownTags()
{
QStringList allTags;
for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) {
allTags.append(tagWidget->getTagName());
}
// Remove duplicates by calling 'removeDuplicates'
allTags.removeDuplicates();
return allTags;
}

View file

@ -12,12 +12,16 @@ class VisualDeckStorageTagFilterWidget : public QWidget
public:
explicit VisualDeckStorageTagFilterWidget(VisualDeckStorageWidget *_parent);
void refreshTags();
QList<DeckPreviewWidget *> filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const;
QStringList gatherAllTags();
void filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const;
void removeTagsNotInList(const QStringList &tags);
void addTagsIfNotPresent(const QStringList &tags);
void addTagIfNotPresent(const QString &tag);
QStringList getAllKnownTags();
VisualDeckStorageWidget *parent;
public slots:
void refreshTags();
};
#endif // VISUAL_DECK_STORAGE_TAG_FILTER_WIDGET_H

View file

@ -3,6 +3,7 @@
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../settings/cache_settings.h"
#include "deck_preview/deck_preview_widget.h"
#include "visual_deck_storage_folder_display_widget.h"
#include "visual_deck_storage_search_widget.h"
#include "visual_deck_storage_sort_widget.h"
#include "visual_deck_storage_tag_filter_widget.h"
@ -36,11 +37,19 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
searchAndSortLayout->addWidget(searchWidget);
layout->addLayout(searchAndSortLayout);
layout->addWidget(tagFilterWidget);
cardSizeWidget = new CardSizeWidget(this, nullptr, SettingsCache::instance().getVisualDeckStorageCardSize());
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
layout->addWidget(flowWidget);
scrollArea = new QScrollArea(this);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
folderWidget = new VisualDeckStorageFolderDisplayWidget(this, this, SettingsCache::instance().getDeckPath());
scrollArea->setWidget(folderWidget);
scrollArea->setWidgetResizable(true);
layout->addWidget(scrollArea);
cardSizeWidget = new CardSizeWidget(this, flowWidget, SettingsCache::instance().getVisualDeckStorageCardSize());
layout->addWidget(cardSizeWidget);
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
@ -56,7 +65,7 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
refreshBannerCards();
databaseLoadIndicator->setVisible(false);
} else {
flowWidget->addWidget(databaseLoadIndicator);
scrollArea->setWidget(databaseLoadIndicator);
}
}
@ -83,82 +92,14 @@ void VisualDeckStorageWidget::deckPreviewDoubleClickedEvent(QMouseEvent *event,
void VisualDeckStorageWidget::refreshBannerCards()
{
QStringList allFiles;
QList<DeckPreviewWidget *> allDecks;
// QDirIterator with QDir::Files and QDir::NoSymLinks ensures only files are listed (no directories or symlinks)
QDirIterator it(SettingsCache::instance().getDeckPath(), QDir::Files | QDir::NoSymLinks,
QDirIterator::Subdirectories);
while (it.hasNext()) {
allFiles << it.next(); // Add each file path to the list
}
for (const QString &file : allFiles) {
auto *display = new DeckPreviewWidget(this, file);
connect(display, &DeckPreviewWidget::deckPreviewClicked, this,
&VisualDeckStorageWidget::deckPreviewClickedEvent);
connect(display, &DeckPreviewWidget::deckPreviewDoubleClicked, this,
&VisualDeckStorageWidget::deckPreviewDoubleClickedEvent);
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, display->bannerCardDisplayWidget,
&CardInfoPictureWidget::setScaleFactor);
display->bannerCardDisplayWidget->setScaleFactor(cardSizeWidget->getSlider()->value());
allDecks.append(display);
}
auto filteredByColorIdentity =
deckPreviewColorIdentityFilterWidget->filterWidgets(sortWidget->filterFiles(allDecks));
auto filteredByTags = tagFilterWidget->filterDecksBySelectedTags(filteredByColorIdentity);
auto filteredFiles = searchWidget->filterFiles(filteredByTags, searchWidget->getSearchText());
tagFilterWidget->removeTagsNotInList(gatherAllTags(filteredFiles));
tagFilterWidget->addTagsIfNotPresent(gatherAllTags(filteredFiles));
flowWidget->clearLayout(); // Clear existing widgets in the flow layout
for (DeckPreviewWidget *deck : filteredFiles) {
flowWidget->addWidget(deck);
}
emit bannerCardsRefreshed();
folderWidget = new VisualDeckStorageFolderDisplayWidget(this, this, SettingsCache::instance().getDeckPath());
scrollArea->setWidget(folderWidget);
}
QStringList VisualDeckStorageWidget::gatherAllTagsFromFlowWidget() const
void VisualDeckStorageWidget::updateTagFilter()
{
QStringList allTags;
if (flowWidget) {
// Iterate through all DeckPreviewWidgets
for (DeckPreviewWidget *display : flowWidget->findChildren<DeckPreviewWidget *>()) {
// Get tags from each DeckPreviewWidget
QStringList tags = display->deckLoader->getTags();
// Add tags to the list while avoiding duplicates
allTags.append(tags);
}
if (folderWidget) {
tagFilterWidget->filterDecksBySelectedTags(folderWidget->findChildren<DeckPreviewWidget *>());
}
// Remove duplicates by calling 'removeDuplicates'
allTags.removeDuplicates();
return allTags;
}
QStringList VisualDeckStorageWidget::gatherAllTags(const QList<DeckPreviewWidget *> &allDecks)
{
QStringList allTags;
// Iterate through all decks provided as input
for (DeckPreviewWidget *deck : allDecks) {
QStringList tags = deck->deckLoader->getTags();
// Add tags to the list while avoiding duplicates
allTags.append(tags);
}
// Remove duplicates
allTags.removeDuplicates();
return allTags;
emit tagFilterUpdated();
}

View file

@ -6,6 +6,7 @@
#include "../cards/card_size_widget.h"
#include "deck_preview/deck_preview_color_identity_filter_widget.h"
#include "deck_preview/deck_preview_widget.h"
#include "visual_deck_storage_folder_display_widget.h"
#include "visual_deck_storage_search_widget.h"
#include "visual_deck_storage_sort_widget.h"
#include "visual_deck_storage_tag_filter_widget.h"
@ -15,6 +16,7 @@
class VisualDeckStorageSearchWidget;
class VisualDeckStorageSortWidget;
class VisualDeckStorageTagFilterWidget;
class VisualDeckStorageFolderDisplayWidget;
class DeckPreviewColorIdentityFilterWidget;
class VisualDeckStorageWidget final : public QWidget
{
@ -22,13 +24,14 @@ class VisualDeckStorageWidget final : public QWidget
public:
explicit VisualDeckStorageWidget(QWidget *parent);
void retranslateUi();
CardSizeWidget *cardSizeWidget;
VisualDeckStorageTagFilterWidget *tagFilterWidget;
public slots:
void deckPreviewClickedEvent(QMouseEvent *event, DeckPreviewWidget *instance);
void deckPreviewDoubleClickedEvent(QMouseEvent *event, DeckPreviewWidget *instance);
void refreshBannerCards(); // Refresh the display of cards based on the current sorting option
QStringList gatherAllTagsFromFlowWidget() const;
QStringList gatherAllTags(const QList<DeckPreviewWidget *> &allDecks);
void updateTagFilter();
void updateSortOrder();
signals:
@ -36,19 +39,18 @@ signals:
void deckPreviewClicked(QMouseEvent *event, DeckPreviewWidget *instance);
void deckPreviewDoubleClicked(QMouseEvent *event, DeckPreviewWidget *instance);
void deckLoadRequested(QString &filePath);
void tagFilterUpdated();
private:
QVBoxLayout *layout;
QHBoxLayout *searchAndSortLayout;
FlowWidget *flowWidget;
DeckListModel *deckListModel;
QLabel *databaseLoadIndicator;
VisualDeckStorageSortWidget *sortWidget;
VisualDeckStorageSearchWidget *searchWidget;
VisualDeckStorageTagFilterWidget *tagFilterWidget;
DeckPreviewColorIdentityFilterWidget *deckPreviewColorIdentityFilterWidget;
CardSizeWidget *cardSizeWidget;
QScrollArea *scrollArea;
VisualDeckStorageFolderDisplayWidget *folderWidget;
};
#endif // VISUAL_DECK_STORAGE_WIDGET_H