From 2c6e7d4d3acf783e3043f35f643734ea99384f76 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Fri, 7 Feb 2025 07:36:24 -0800 Subject: [PATCH] Add option to hide folder structure in Visual Deck Storage (#5575) --- ...ual_deck_storage_folder_display_widget.cpp | 124 +++++++++++++----- ...isual_deck_storage_folder_display_widget.h | 8 +- .../visual_deck_storage_widget.cpp | 38 +++++- .../visual_deck_storage_widget.h | 4 + cockatrice/src/settings/cache_settings.cpp | 7 + cockatrice/src/settings/cache_settings.h | 6 + dbconverter/src/mocks.cpp | 3 + tests/carddatabase/mocks.cpp | 3 + 8 files changed, 150 insertions(+), 43 deletions(-) diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp index 1ac62b784..1c8e0d37c 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp @@ -10,14 +10,16 @@ VisualDeckStorageFolderDisplayWidget::VisualDeckStorageFolderDisplayWidget( QWidget *parent, VisualDeckStorageWidget *_visualDeckStorageWidget, QString _filePath, - bool canBeHidden) - : QWidget(parent), visualDeckStorageWidget(_visualDeckStorageWidget), filePath(_filePath) + bool canBeHidden, + bool _showFolders) + : QWidget(parent), showFolders(_showFolders), visualDeckStorageWidget(_visualDeckStorageWidget), filePath(_filePath) { layout = new QVBoxLayout(this); setLayout(layout); header = new BannerWidget(this, ""); header->setClickable(canBeHidden); + header->setHidden(!showFolders); layout->addWidget(header); container = new QWidget(this); @@ -63,10 +65,26 @@ void VisualDeckStorageFolderDisplayWidget::refreshUi() header->setText(bannerText); } +static QStringList getAllFiles(const QString &filePath, bool recursive) +{ + QStringList allFiles; + + // QDirIterator with QDir::Files ensures only files are listed (no directories) + auto flags = + recursive ? QDirIterator::Subdirectories | QDirIterator::FollowSymlinks : QDirIterator::NoIteratorFlags; + QDirIterator it(filePath, QDir::Files, flags); + + while (it.hasNext()) { + allFiles << it.next(); // Add each file path to the list + } + + return allFiles; +} + void VisualDeckStorageFolderDisplayWidget::createWidgetsForFiles() { QList allDecks; - for (const QString &file : getAllFiles()) { + for (const QString &file : getAllFiles(filePath, !showFolders)) { auto *display = new DeckPreviewWidget(flowWidget, visualDeckStorageWidget, file); connect(display, &DeckPreviewWidget::deckPreviewClicked, visualDeckStorageWidget, @@ -121,14 +139,82 @@ bool VisualDeckStorageFolderDisplayWidget::checkVisibility() return atLeastOneWidgetVisible; } +static QStringList getAllSubFolders(const QString &filePath) +{ + QStringList allFolders; + + // QDirIterator with QDir::Files ensures only files are listed (no directories) + QDirIterator it(filePath, QDir::Dirs | QDir::NoDotAndDotDot); + + while (it.hasNext()) { + allFolders << it.next(); // Add each file path to the list + } + + return allFolders; +} + void VisualDeckStorageFolderDisplayWidget::createWidgetsForFolders() { - for (const QString &dir : getAllSubFolders()) { - auto *display = new VisualDeckStorageFolderDisplayWidget(this, visualDeckStorageWidget, dir, true); + if (!showFolders) { + return; + } + + for (const QString &dir : getAllSubFolders(filePath)) { + auto *display = new VisualDeckStorageFolderDisplayWidget(this, visualDeckStorageWidget, dir, true, showFolders); containerLayout->addWidget(display); } } +void VisualDeckStorageFolderDisplayWidget::updateShowFolders(bool enabled) +{ + showFolders = enabled; + + if (!showFolders) { + flattenFolderStructure(); + } else { + // if setting was switched from disabled to enabled, we assume that there isn't any existing subfolders + createWidgetsForFiles(); + createWidgetsForFolders(); + } + + header->setHidden(!showFolders); +} + +/** + * Recursively gets all DeckPreviewWidgets in this folder and its subfolders + */ +static QList getAllDecksRecursive(VisualDeckStorageFolderDisplayWidget *folder) +{ + QList allDecks; + + if (auto *flowWidget = folder->getFlowWidget()) { + // Iterate through all DeckPreviewWidgets in this folder + allDecks << flowWidget->findChildren(); + } + + for (auto *subFolder : folder->findChildren()) { + allDecks << getAllDecksRecursive(subFolder); + } + + return allDecks; +} + +/** + * Recursively steals all DeckPreviewWidgets from this widget's subfolders, and deletes all subfolders + */ +void VisualDeckStorageFolderDisplayWidget::flattenFolderStructure() +{ + for (VisualDeckStorageFolderDisplayWidget *subFolder : findChildren()) { + // steal all DeckPreviewWidgets from the subfolder and all its subfolders + for (auto *deck : getAllDecksRecursive(subFolder)) { + flowWidget->addWidget(deck); + } + + // delete the subfolder + subFolder->deleteLater(); + } +} + QStringList VisualDeckStorageFolderDisplayWidget::gatherAllTagsFromFlowWidget() const { QStringList allTags; @@ -148,32 +234,4 @@ QStringList VisualDeckStorageFolderDisplayWidget::gatherAllTagsFromFlowWidget() allTags.removeDuplicates(); return allTags; -} - -QStringList VisualDeckStorageFolderDisplayWidget::getAllFiles() const -{ - QStringList allFiles; - - // QDirIterator with QDir::Files ensures only files are listed (no directories) - QDirIterator it(filePath, QDir::Files); - - 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 ensures only files are listed (no directories) - QDirIterator it(filePath, QDir::Dirs | QDir::NoDotAndDotDot); - - while (it.hasNext()) { - allFolders << it.next(); // Add each file path to the list - } - - return allFolders; } \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.h index 7e1279e6b..d149e4d27 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.h @@ -15,13 +15,13 @@ public: VisualDeckStorageFolderDisplayWidget(QWidget *parent, VisualDeckStorageWidget *_visualDeckStorageWidget, QString _filePath, - bool canBeHidden); + bool canBeHidden, + bool _showFolders); void refreshUi(); void createWidgetsForFiles(); void createWidgetsForFolders(); + void flattenFolderStructure(); QStringList gatherAllTagsFromFlowWidget() const; - [[nodiscard]] QStringList getAllFiles() const; - [[nodiscard]] QStringList getAllSubFolders() const; FlowWidget *getFlowWidget() const { return flowWidget; @@ -30,8 +30,10 @@ public: public slots: void updateVisibility(); bool checkVisibility(); + void updateShowFolders(bool enabled); private: + bool showFolders; QVBoxLayout *layout; VisualDeckStorageWidget *visualDeckStorageWidget; QString filePath; diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp index cae8d65c8..6e0203a3e 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp @@ -23,6 +23,7 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare layout->setContentsMargins(9, 0, 9, 5); setLayout(layout); + // search bar row searchAndSortLayout = new QHBoxLayout(this); searchAndSortLayout->setSpacing(3); searchAndSortLayout->setContentsMargins(9, 0, 9, 0); @@ -30,26 +31,42 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare deckPreviewColorIdentityFilterWidget = new DeckPreviewColorIdentityFilterWidget(this); sortWidget = new VisualDeckStorageSortWidget(this); searchWidget = new VisualDeckStorageSearchWidget(this); - tagFilterWidget = new VisualDeckStorageTagFilterWidget(this); searchAndSortLayout->addWidget(deckPreviewColorIdentityFilterWidget); searchAndSortLayout->addWidget(sortWidget); searchAndSortLayout->addWidget(searchWidget); - layout->addLayout(searchAndSortLayout); - layout->addWidget(tagFilterWidget); + + // checkbox row + QHBoxLayout *checkBoxLayout = new QHBoxLayout(this); + checkBoxLayout->setContentsMargins(9, 0, 9, 0); + + showFoldersCheckBox = new QCheckBox(this); + showFoldersCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowFolders()); + connect(showFoldersCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setVisualDeckStorageShowFolders); + + checkBoxLayout->addWidget(showFoldersCheckBox); + checkBoxLayout->addStretch(); + + // tag filter box + tagFilterWidget = new VisualDeckStorageTagFilterWidget(this); + + // card size slider cardSizeWidget = new CardSizeWidget(this, nullptr, SettingsCache::instance().getVisualDeckStorageCardSize()); + // deck area scrollArea = new QScrollArea(this); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - folderWidget = new VisualDeckStorageFolderDisplayWidget(this, this, SettingsCache::instance().getDeckPath(), false); - scrollArea->setWidget(folderWidget); scrollArea->setWidgetResizable(true); + // putting everything together + layout->addLayout(searchAndSortLayout); + layout->addLayout(checkBoxLayout); + layout->addWidget(tagFilterWidget); layout->addWidget(scrollArea); - layout->addWidget(cardSizeWidget); connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this, @@ -90,6 +107,8 @@ void VisualDeckStorageWidget::resizeEvent(QResizeEvent *event) void VisualDeckStorageWidget::retranslateUi() { databaseLoadIndicator->setText(tr("Loading database ...")); + + showFoldersCheckBox->setText(tr("Show Folders")); } void VisualDeckStorageWidget::deckPreviewClickedEvent(QMouseEvent *event, DeckPreviewWidget *instance) @@ -105,7 +124,12 @@ void VisualDeckStorageWidget::deckPreviewDoubleClickedEvent(QMouseEvent *event, void VisualDeckStorageWidget::createRootFolderWidget() { - folderWidget = new VisualDeckStorageFolderDisplayWidget(this, this, SettingsCache::instance().getDeckPath(), false); + folderWidget = new VisualDeckStorageFolderDisplayWidget(this, this, SettingsCache::instance().getDeckPath(), false, + showFoldersCheckBox->isChecked()); + + connect(showFoldersCheckBox, &QCheckBox::QT_STATE_CHANGED, folderWidget, + &VisualDeckStorageFolderDisplayWidget::updateShowFolders); + scrollArea->setWidget(folderWidget); scrollArea->widget()->setMaximumWidth(scrollArea->viewport()->width()); scrollArea->widget()->adjustSize(); diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.h index 2d3a4b322..7c65fdafe 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.h @@ -11,6 +11,7 @@ #include "visual_deck_storage_sort_widget.h" #include "visual_deck_storage_tag_filter_widget.h" +#include #include class VisualDeckStorageSearchWidget; @@ -56,6 +57,9 @@ private: VisualDeckStorageSortWidget *sortWidget; VisualDeckStorageSearchWidget *searchWidget; DeckPreviewColorIdentityFilterWidget *deckPreviewColorIdentityFilterWidget; + + QCheckBox *showFoldersCheckBox; + QScrollArea *scrollArea; VisualDeckStorageFolderDisplayWidget *folderWidget; }; diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp index b4532f3d3..bbe228c52 100644 --- a/cockatrice/src/settings/cache_settings.cpp +++ b/cockatrice/src/settings/cache_settings.cpp @@ -266,6 +266,7 @@ SettingsCache::SettingsCache() settings->value("cards/printingselectornavigationbuttonsvisible", true).toBool(); visualDeckStorageCardSize = settings->value("cards/visualdeckstoragecardsize", 100).toInt(); visualDeckStorageSortingOrder = settings->value("interface/visualdeckstoragesortingorder", 0).toInt(); + visualDeckStorageShowFolders = settings->value("interface/visualdeckstorageshowfolders", true).toBool(); visualDeckStorageDrawUnusedColorIdentities = settings->value("interface/visualdeckstoragedrawunusedcoloridentities", true).toBool(); visualDeckStorageUnusedColorIdentitiesOpacity = @@ -678,6 +679,12 @@ void SettingsCache::setVisualDeckStorageSortingOrder(int _visualDeckStorageSorti settings->setValue("interface/visualdeckstoragesortingorder", visualDeckStorageSortingOrder); } +void SettingsCache::setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T value) +{ + visualDeckStorageShowFolders = value; + settings->setValue("interface/visualdeckstorageshowfolders", visualDeckStorageShowFolders); +} + void SettingsCache::setVisualDeckStorageCardSize(int _visualDeckStorageCardSize) { visualDeckStorageCardSize = _visualDeckStorageCardSize; diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h index cc5489dba..b2800e9f5 100644 --- a/cockatrice/src/settings/cache_settings.h +++ b/cockatrice/src/settings/cache_settings.h @@ -131,6 +131,7 @@ private: bool printingSelectorCardSizeSliderVisible; bool printingSelectorNavigationButtonsVisible; int visualDeckStorageSortingOrder; + bool visualDeckStorageShowFolders; int visualDeckStorageCardSize; bool visualDeckStorageDrawUnusedColorIdentities; int visualDeckStorageUnusedColorIdentitiesOpacity; @@ -416,6 +417,10 @@ public: { return visualDeckStorageSortingOrder; } + bool getVisualDeckStorageShowFolders() const + { + return visualDeckStorageShowFolders; + } int getVisualDeckStorageCardSize() const { return visualDeckStorageCardSize; @@ -761,6 +766,7 @@ public slots: void setPrintingSelectorCardSizeSliderVisible(QT_STATE_CHANGED_T _cardSizeSliderVisible); void setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED_T _navigationButtonsVisible); void setVisualDeckStorageSortingOrder(int _visualDeckStorageSortingOrder); + void setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T value); void setVisualDeckStorageCardSize(int _visualDeckStorageCardSize); void setVisualDeckStorageDrawUnusedColorIdentities(QT_STATE_CHANGED_T _visualDeckStorageDrawUnusedColorIdentities); void setVisualDeckStorageUnusedColorIdentitiesOpacity(int _visualDeckStorageUnusedColorIdentitiesOpacity); diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp index ac95c9987..7ed679f03 100644 --- a/dbconverter/src/mocks.cpp +++ b/dbconverter/src/mocks.cpp @@ -211,6 +211,9 @@ void SettingsCache::setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED void SettingsCache::setVisualDeckStorageSortingOrder(int /* _visualDeckStorageSortingOrder */) { } +void SettingsCache::setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T /* value */) +{ +} void SettingsCache::setVisualDeckStorageCardSize(int /* _visualDeckStorageCardSize */) { } diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp index 782735ac2..66f451683 100644 --- a/tests/carddatabase/mocks.cpp +++ b/tests/carddatabase/mocks.cpp @@ -215,6 +215,9 @@ void SettingsCache::setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED void SettingsCache::setVisualDeckStorageSortingOrder(int /* _visualDeckStorageSortingOrder */) { } +void SettingsCache::setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T /* value */) +{ +} void SettingsCache::setVisualDeckStorageCardSize(int /* _visualDeckStorageCardSize */) { }