From a0b52ce450419a075eeeadccec6d0250d29bcf7d Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Sun, 2 Feb 2025 16:08:04 +0100 Subject: [PATCH] Implement folder support for VDS. (#5545) --- cockatrice/CMakeLists.txt | 1 + .../widgets/general/display/banner_widget.cpp | 14 +- .../widgets/general/display/banner_widget.h | 8 + ...k_preview_color_identity_filter_widget.cpp | 18 +- ...eck_preview_color_identity_filter_widget.h | 2 +- .../deck_preview_tag_addition_widget.cpp | 2 +- .../deck_preview/deck_preview_widget.cpp | 24 ++- .../deck_preview/deck_preview_widget.h | 12 +- ...ual_deck_storage_folder_display_widget.cpp | 179 ++++++++++++++++++ ...isual_deck_storage_folder_display_widget.h | 44 +++++ .../visual_deck_storage_search_widget.cpp | 25 +-- .../visual_deck_storage_search_widget.h | 2 +- .../visual_deck_storage_sort_widget.cpp | 19 +- .../visual_deck_storage_sort_widget.h | 4 +- .../visual_deck_storage_tag_filter_widget.cpp | 58 +++++- .../visual_deck_storage_tag_filter_widget.h | 9 +- .../visual_deck_storage_widget.cpp | 133 ++++++------- .../visual_deck_storage_widget.h | 20 +- 18 files changed, 438 insertions(+), 136 deletions(-) create mode 100644 cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 04a7c52c4..4b4567e95 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -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 diff --git a/cockatrice/src/client/ui/widgets/general/display/banner_widget.cpp b/cockatrice/src/client/ui/widgets/general/display/banner_widget.cpp index cf1959d7a..64a64ba7c 100644 --- a/cockatrice/src/client/ui/widgets/general/display/banner_widget.cpp +++ b/cockatrice/src/client/ui/widgets/general/display/banner_widget.cpp @@ -14,20 +14,26 @@ BannerWidget::BannerWidget(QWidget *parent, const QString &text, Qt::Orientation bannerLabel->setStyleSheet("font-size: 24px; font-weight: bold; color: white;"); // Layout to center the banner label - QVBoxLayout *layout = new QVBoxLayout(this); + layout = new QVBoxLayout(this); layout->addWidget(bannerLabel); - layout->setContentsMargins(0, 20, 0, 20); // Space for the gradient setLayout(layout); // Set minimum height for the widget - setMinimumHeight(100); + setMinimumHeight(50); connect(this, &BannerWidget::buddyVisibilityChanged, this, &BannerWidget::toggleBuddyVisibility); } void BannerWidget::mousePressEvent(QMouseEvent *event) { QWidget::mousePressEvent(event); - emit buddyVisibilityChanged(); + if (clickable) { + emit buddyVisibilityChanged(); + } +} + +void BannerWidget::setText(const QString &text) const +{ + bannerLabel->setText(text); } void BannerWidget::toggleBuddyVisibility() const diff --git a/cockatrice/src/client/ui/widgets/general/display/banner_widget.h b/cockatrice/src/client/ui/widgets/general/display/banner_widget.h index 4947ffd58..545cb5ed6 100644 --- a/cockatrice/src/client/ui/widgets/general/display/banner_widget.h +++ b/cockatrice/src/client/ui/widgets/general/display/banner_widget.h @@ -2,6 +2,7 @@ #define BANNER_WIDGET_H #include +#include #include class BannerWidget : public QWidget @@ -14,6 +15,7 @@ public: Qt::Orientation orientation = Qt::Vertical, int transparency = 80); void mousePressEvent(QMouseEvent *event) override; + void setText(const QString &text) const; void setBuddy(QWidget *_buddy) { buddy = _buddy; @@ -22,15 +24,21 @@ public: { return bannerLabel->text(); } + void setClickable(bool _clickable) + { + clickable = _clickable; + } protected: void paintEvent(QPaintEvent *event) override; private: + QVBoxLayout *layout; QLabel *bannerLabel; Qt::Orientation gradientOrientation; int transparency; // Transparency percentage for the gradient QWidget *buddy; + bool clickable = true; signals: void buddyVisibilityChanged(); private slots: diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp index 9b92f8994..d854e1588 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp @@ -112,9 +112,9 @@ DeckPreviewColorIdentityFilterWidget::DeckPreviewColorIdentityFilterWidget(Visua // Connect the button's toggled signal connect(toggleButton, &QPushButton::toggled, this, &DeckPreviewColorIdentityFilterWidget::updateFilterMode); connect(this, &DeckPreviewColorIdentityFilterWidget::activeColorsChanged, parent, - &VisualDeckStorageWidget::refreshBannerCards); + &VisualDeckStorageWidget::updateColorFilter); connect(this, &DeckPreviewColorIdentityFilterWidget::filterModeChanged, parent, - &VisualDeckStorageWidget::refreshBannerCards); + &VisualDeckStorageWidget::updateColorFilter); // Call retranslateUi to set the initial text retranslateUi(); @@ -139,10 +139,8 @@ void DeckPreviewColorIdentityFilterWidget::updateFilterMode(bool checked) emit filterModeChanged(exactMatchMode); } -QList DeckPreviewColorIdentityFilterWidget::filterWidgets(QList &widgets) +void DeckPreviewColorIdentityFilterWidget::filterWidgets(QList widgets) { - QList filteredWidgets; - // Check if no colors are active bool noColorsActive = true; for (auto it = activeColors.constBegin(); it != activeColors.constEnd(); ++it) { @@ -154,7 +152,9 @@ QList DeckPreviewColorIdentityFilterWidget::filterWidgets(Q // If no colors are active, return the unfiltered list of widgets if (noColorsActive) { - return widgets; + for (DeckPreviewWidget *previewWidget : widgets) { + previewWidget->filteredByColor = false; + } } for (const auto &widget : widgets) { @@ -192,10 +192,6 @@ QList DeckPreviewColorIdentityFilterWidget::filterWidgets(Q } } - if (matchesFilter) { - filteredWidgets << widget; - } + widget->filteredByColor = !matchesFilter; } - - return filteredWidgets; } diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.h index e18bf8860..3c9bdb70c 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.h @@ -42,7 +42,7 @@ class DeckPreviewColorIdentityFilterWidget : public QWidget public: explicit DeckPreviewColorIdentityFilterWidget(VisualDeckStorageWidget *parent); void retranslateUi(); - QList filterWidgets(QList &widgets); + void filterWidgets(QList widgets); signals: void filterModeChanged(bool exactMatchMode); diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp index 4f4304e05..16a87bc8d 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp @@ -39,7 +39,7 @@ void DeckPreviewTagAdditionWidget::mousePressEvent(QMouseEvent *event) emit tagClicked(); } QWidget::mousePressEvent(event); - QStringList knownTags = parent->parent->parent->gatherAllTagsFromFlowWidget(); + QStringList knownTags = parent->parent->visualDeckStorageWidget->tagFilterWidget->getAllKnownTags(); QStringList activeTags = parent->deckLoader->getTags(); bool canAddTags = true; diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp index 59e02ccbc..ecd4f4a79 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp @@ -9,14 +9,18 @@ #include #include -DeckPreviewWidget::DeckPreviewWidget(VisualDeckStorageWidget *_parent, const QString &_filePath) - : QWidget(_parent), parent(_parent), filePath(_filePath) +DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent, + VisualDeckStorageWidget *_visualDeckStorageWidget, + const QString &_filePath) + : QWidget(_parent), visualDeckStorageWidget(_visualDeckStorageWidget), filePath(_filePath) { layout = new QVBoxLayout(this); setLayout(layout); deckLoader = new DeckLoader(); connect(deckLoader, &DeckLoader::loadFinished, this, &DeckPreviewWidget::initializeUi); + connect(deckLoader, &DeckLoader::loadFinished, visualDeckStorageWidget->tagFilterWidget, + &VisualDeckStorageTagFilterWidget::refreshTags); deckLoader->loadFromFileAsync(filePath, DeckLoader::getFormatFromName(filePath), false); bannerCardDisplayWidget = new DeckPreviewCardPictureWidget(this); @@ -52,6 +56,22 @@ void DeckPreviewWidget::initializeUi(const bool deckLoadSuccess) layout->addWidget(deckTagsDisplayWidget); } +void DeckPreviewWidget::updateVisibility() +{ + if (isVisible() != checkVisibility()) { + setHidden(!checkVisibility()); + emit visibilityUpdated(); + } +} + +bool DeckPreviewWidget::checkVisibility() const +{ + if (filteredBySearch || filteredByColor || filteredByTags) { + return false; + } + return true; +} + QString DeckPreviewWidget::getColorIdentity() { QStringList cardList = deckLoader->getCardList(); diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h index 91a2060a3..8be7d07ac 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h @@ -16,20 +16,27 @@ class DeckPreviewWidget final : public QWidget { Q_OBJECT public: - explicit DeckPreviewWidget(VisualDeckStorageWidget *_parent, const QString &_filePath); + explicit DeckPreviewWidget(QWidget *_parent, + VisualDeckStorageWidget *_visualDeckStorageWidget, + const QString &_filePath); QString getColorIdentity(); - VisualDeckStorageWidget *parent; + VisualDeckStorageWidget *visualDeckStorageWidget; QVBoxLayout *layout; QString filePath; DeckLoader *deckLoader; DeckPreviewCardPictureWidget *bannerCardDisplayWidget; DeckPreviewColorIdentityWidget *colorIdentityWidget; DeckPreviewDeckTagsDisplayWidget *deckTagsDisplayWidget; + bool filteredBySearch = false; + bool filteredByColor = false; + bool filteredByTags = false; + bool checkVisibility() const; signals: void deckPreviewClicked(QMouseEvent *event, DeckPreviewWidget *instance); void deckPreviewDoubleClicked(QMouseEvent *event, DeckPreviewWidget *instance); + void visibilityUpdated(); public slots: void setFilePath(const QString &filePath); @@ -37,6 +44,7 @@ public slots: void imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance); void imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance); void initializeUi(bool deckLoadSuccess); + void updateVisibility(); }; #endif // DECK_PREVIEW_WIDGET_H 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 new file mode 100644 index 000000000..1ac62b784 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp @@ -0,0 +1,179 @@ +#include "visual_deck_storage_folder_display_widget.h" + +#include "../../../../settings/cache_settings.h" +#include "deck_preview/deck_preview_widget.h" + +#include +#include + +VisualDeckStorageFolderDisplayWidget::VisualDeckStorageFolderDisplayWidget( + QWidget *parent, + VisualDeckStorageWidget *_visualDeckStorageWidget, + QString _filePath, + bool canBeHidden) + : QWidget(parent), visualDeckStorageWidget(_visualDeckStorageWidget), filePath(_filePath) +{ + layout = new QVBoxLayout(this); + setLayout(layout); + + header = new BannerWidget(this, ""); + header->setClickable(canBeHidden); + 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); + + connect(visualDeckStorageWidget, &VisualDeckStorageWidget::tagFilterUpdated, this, + &VisualDeckStorageFolderDisplayWidget::updateVisibility); + connect(visualDeckStorageWidget, &VisualDeckStorageWidget::colorFilterUpdated, this, + &VisualDeckStorageFolderDisplayWidget::updateVisibility); + connect(visualDeckStorageWidget, &VisualDeckStorageWidget::searchFilterUpdated, this, + &VisualDeckStorageFolderDisplayWidget::updateVisibility); + + createWidgetsForFiles(); + createWidgetsForFolders(); + + refreshUi(); +} + +void VisualDeckStorageFolderDisplayWidget::refreshUi() +{ + QString bannerText = tr("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->setText(bannerText); +} + +void VisualDeckStorageFolderDisplayWidget::createWidgetsForFiles() +{ + QList allDecks; + for (const QString &file : getAllFiles()) { + auto *display = new DeckPreviewWidget(flowWidget, 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); + display->bannerCardDisplayWidget->setScaleFactor(visualDeckStorageWidget->cardSizeWidget->getSlider()->value()); + allDecks.append(display); + } + + flowWidget->clearLayout(); // Clear existing widgets in the flow layout + + for (DeckPreviewWidget *deck : allDecks) { + flowWidget->addWidget(deck); + } +} + +void VisualDeckStorageFolderDisplayWidget::updateVisibility() +{ + bool atLeastOneWidgetVisible = checkVisibility(); + if (atLeastOneWidgetVisible) { + setVisible(true); + for (DeckPreviewWidget *display : flowWidget->findChildren()) { + display->updateVisibility(); + } + for (VisualDeckStorageFolderDisplayWidget *subFolder : findChildren()) { + subFolder->updateVisibility(); + } + } else { + setVisible(false); + } +} + +bool VisualDeckStorageFolderDisplayWidget::checkVisibility() +{ + bool atLeastOneWidgetVisible = false; + if (flowWidget) { + // Iterate through all DeckPreviewWidgets + for (DeckPreviewWidget *display : flowWidget->findChildren()) { + if (display->checkVisibility()) { + atLeastOneWidgetVisible = true; + } + } + } + for (VisualDeckStorageFolderDisplayWidget *subFolder : findChildren()) { + if (subFolder->checkVisibility()) { + atLeastOneWidgetVisible = true; + } + } + return atLeastOneWidgetVisible; +} + +void VisualDeckStorageFolderDisplayWidget::createWidgetsForFolders() +{ + for (const QString &dir : getAllSubFolders()) { + auto *display = new VisualDeckStorageFolderDisplayWidget(this, visualDeckStorageWidget, dir, true); + containerLayout->addWidget(display); + } +} + +QStringList VisualDeckStorageFolderDisplayWidget::gatherAllTagsFromFlowWidget() const +{ + QStringList allTags; + + if (flowWidget) { + // Iterate through all DeckPreviewWidgets + for (DeckPreviewWidget *display : flowWidget->findChildren()) { + // 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 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 new file mode 100644 index 000000000..7e1279e6b --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.h @@ -0,0 +1,44 @@ +#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 +#include + +class VisualDeckStorageFolderDisplayWidget : public QWidget +{ + Q_OBJECT +public: + VisualDeckStorageFolderDisplayWidget(QWidget *parent, + VisualDeckStorageWidget *_visualDeckStorageWidget, + QString _filePath, + bool canBeHidden); + void refreshUi(); + void createWidgetsForFiles(); + void createWidgetsForFolders(); + QStringList gatherAllTagsFromFlowWidget() const; + [[nodiscard]] QStringList getAllFiles() const; + [[nodiscard]] QStringList getAllSubFolders() const; + FlowWidget *getFlowWidget() const + { + return flowWidget; + }; + +public slots: + void updateVisibility(); + bool checkVisibility(); + +private: + QVBoxLayout *layout; + VisualDeckStorageWidget *visualDeckStorageWidget; + QString filePath; + BannerWidget *header; + QWidget *container; + QVBoxLayout *containerLayout; + FlowWidget *flowWidget; +}; + +#endif // VISUAL_DECK_STORAGE_FOLDER_DISPLAY_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp index 7828a76f4..8b6006a31 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp @@ -25,7 +25,7 @@ VisualDeckStorageSearchWidget::VisualDeckStorageSearchWidget(VisualDeckStorageWi searchDebounceTimer->start(300); // 300ms debounce }); - connect(searchDebounceTimer, &QTimer::timeout, parent, &VisualDeckStorageWidget::refreshBannerCards); + connect(searchDebounceTimer, &QTimer::timeout, parent, &VisualDeckStorageWidget::updateSearchFilter); } /** @@ -38,23 +38,18 @@ QString VisualDeckStorageSearchWidget::getSearchText() return searchBar->text(); } -QList VisualDeckStorageSearchWidget::filterFiles(QList &widgets, - const QString &searchText) +void VisualDeckStorageSearchWidget::filterWidgets(QList widgets, const QString &searchText) { if (searchText.isEmpty() || searchText.isNull()) { - return widgets; - } - - QList filteredWidgets; - - for (const auto &file : widgets) { - QFileInfo fileInfo(file->filePath); - QString fileName = fileInfo.fileName().toLower(); - - if (fileName.contains(searchText.toLower())) { - filteredWidgets << file; + for (auto widget : widgets) { + widget->filteredBySearch = false; } } - return filteredWidgets; + for (auto file : widgets) { + QFileInfo fileInfo(file->filePath); + QString fileName = fileInfo.fileName().toLower(); + + file->filteredBySearch = !fileName.contains(searchText.toLower()); + } } diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.h index 880cc4a47..e8b91bc5b 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.h @@ -16,7 +16,7 @@ class VisualDeckStorageSearchWidget : public QWidget public: explicit VisualDeckStorageSearchWidget(VisualDeckStorageWidget *parent); QString getSearchText(); - QList filterFiles(QList &widgets, const QString &searchText); + void filterWidgets(QList widgets, const QString &searchText); private: QHBoxLayout *layout; diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp index 9dfcc0870..a53e84a27 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp @@ -63,7 +63,24 @@ void VisualDeckStorageSortWidget::updateSortOrder() emit sortOrderChanged(); } -QList &VisualDeckStorageSortWidget::filterFiles(QList &widgets) +void VisualDeckStorageSortWidget::sortFolder(VisualDeckStorageFolderDisplayWidget *folderWidget) +{ + auto children = + folderWidget->getFlowWidget()->findChildren(QString(), Qt::FindChildOption::FindDirectChildrenOnly); + for (auto widget : children) { + auto deckPreviewWidgets = + widget->findChildren(QString(), Qt::FindChildOption::FindDirectChildrenOnly); + auto newOrder = filterFiles(deckPreviewWidgets); + for (DeckPreviewWidget *previewWidget : newOrder) { + folderWidget->getFlowWidget()->removeWidget(previewWidget); + } + for (DeckPreviewWidget *previewWidget : newOrder) { + folderWidget->getFlowWidget()->addWidget(previewWidget); + } + } +} + +QList VisualDeckStorageSortWidget::filterFiles(QList widgets) { // Sort the widgets list based on the current sort order std::sort(widgets.begin(), widgets.end(), [this](DeckPreviewWidget *widget1, DeckPreviewWidget *widget2) { diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.h index 58838f3ac..1aac55d2d 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.h @@ -8,6 +8,7 @@ #include class VisualDeckStorageWidget; +class VisualDeckStorageFolderDisplayWidget; class VisualDeckStorageSortWidget : public QWidget { Q_OBJECT @@ -16,8 +17,9 @@ public: explicit VisualDeckStorageSortWidget(VisualDeckStorageWidget *parent); void retranslateUi(); void updateSortOrder(); + void sortFolder(VisualDeckStorageFolderDisplayWidget *folderWidget); QString getSearchText(); - QList &filterFiles(QList &widgets); + QList filterFiles(QList widgets); signals: void sortOrderChanged(); diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp index 87ffac90c..97b810d21 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp @@ -25,8 +25,13 @@ VisualDeckStorageTagFilterWidget::VisualDeckStorageTagFilterWidget(VisualDeckSto layout->addWidget(flowWidget); } -QList -VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QList &deckPreviews) const +void VisualDeckStorageTagFilterWidget::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + refreshTags(); +} + +void VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QList &deckPreviews) const { // Collect selected tags from DeckPreviewTagDisplayWidget QStringList selectedTags; @@ -36,9 +41,12 @@ VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QListfilteredByTags = false; + } + return; } // Filter DeckPreviewWidgets that contain all of the selected tags @@ -50,12 +58,15 @@ VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QListfilteredByTags = !allTagsPresent; } +} - return filteredDecks; +void VisualDeckStorageTagFilterWidget::refreshTags() +{ + QStringList allTags = gatherAllTags(); + removeTagsNotInList(gatherAllTags()); + addTagsIfNotPresent(gatherAllTags()); } void VisualDeckStorageTagFilterWidget::removeTagsNotInList(const QStringList &tags) @@ -93,8 +104,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->addWidget(newTagWidget); } } + +QStringList VisualDeckStorageTagFilterWidget::gatherAllTags() +{ + QStringList allTags; + QList deckWidgets = parent->findChildren(); + + for (DeckPreviewWidget *widget : deckWidgets) { + if (widget->checkVisibility()) { + allTags << widget->deckLoader->getTags(); + } + } + return allTags; +} + +QStringList VisualDeckStorageTagFilterWidget::getAllKnownTags() +{ + QStringList allTags; + + for (DeckPreviewTagDisplayWidget *tagWidget : findChildren()) { + allTags.append(tagWidget->getTagName()); + } + + // Remove duplicates by calling 'removeDuplicates' + allTags.removeDuplicates(); + + return allTags; +} diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.h index 13fc7ef4d..ce8ca8d14 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.h @@ -12,12 +12,17 @@ class VisualDeckStorageTagFilterWidget : public QWidget public: explicit VisualDeckStorageTagFilterWidget(VisualDeckStorageWidget *_parent); - void refreshTags(); - QList filterDecksBySelectedTags(const QList &deckPreviews) const; + QStringList gatherAllTags(); + void filterDecksBySelectedTags(const QList &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(); + void showEvent(QShowEvent *event) override; }; #endif // VISUAL_DECK_STORAGE_TAG_FILTER_WIDGET_H 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 53c65f30d..b98456144 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 @@ -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" @@ -17,12 +18,12 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare deckListModel = new DeckListModel(this); deckListModel->setObjectName("visualDeckModel"); - layout = new QVBoxLayout(); + layout = new QVBoxLayout(this); layout->setSpacing(0); layout->setContentsMargins(9, 0, 9, 5); setLayout(layout); - searchAndSortLayout = new QHBoxLayout(); + searchAndSortLayout = new QHBoxLayout(this); searchAndSortLayout->setSpacing(3); searchAndSortLayout->setContentsMargins(9, 0, 9, 0); @@ -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(), false); + + 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,20 +65,22 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare refreshBannerCards(); databaseLoadIndicator->setVisible(false); } else { - flowWidget->addWidget(databaseLoadIndicator); + scrollArea->setWidget(databaseLoadIndicator); } } +void VisualDeckStorageWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + scrollArea->widget()->setMaximumWidth(scrollArea->viewport()->width()); + scrollArea->widget()->adjustSize(); +} + void VisualDeckStorageWidget::retranslateUi() { databaseLoadIndicator->setText(tr("Loading database ...")); } -void VisualDeckStorageWidget::updateSortOrder() -{ - refreshBannerCards(); // Refresh the banner cards with the new sort order -} - void VisualDeckStorageWidget::deckPreviewClickedEvent(QMouseEvent *event, DeckPreviewWidget *instance) { emit deckPreviewClicked(event, instance); @@ -83,82 +94,44 @@ void VisualDeckStorageWidget::deckPreviewDoubleClickedEvent(QMouseEvent *event, void VisualDeckStorageWidget::refreshBannerCards() { - QStringList allFiles; - QList 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(), false); + scrollArea->setWidget(folderWidget); + scrollArea->widget()->setMaximumWidth(scrollArea->viewport()->width()); + scrollArea->widget()->adjustSize(); } -QStringList VisualDeckStorageWidget::gatherAllTagsFromFlowWidget() const +void VisualDeckStorageWidget::updateSortOrder() { - QStringList allTags; - - if (flowWidget) { - // Iterate through all DeckPreviewWidgets - for (DeckPreviewWidget *display : flowWidget->findChildren()) { - // Get tags from each DeckPreviewWidget - QStringList tags = display->deckLoader->getTags(); - - // Add tags to the list while avoiding duplicates - allTags.append(tags); + if (folderWidget) { + qDebug() << "Updating sort order"; + sortWidget->sortFolder(folderWidget); + for (VisualDeckStorageFolderDisplayWidget *subFolderWidget : + folderWidget->findChildren()) { + sortWidget->sortFolder(subFolderWidget); } } - - // Remove duplicates by calling 'removeDuplicates' - allTags.removeDuplicates(); - - return allTags; } -QStringList VisualDeckStorageWidget::gatherAllTags(const QList &allDecks) +void VisualDeckStorageWidget::updateTagFilter() { - 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); + if (folderWidget) { + tagFilterWidget->filterDecksBySelectedTags(folderWidget->findChildren()); } - - // Remove duplicates - allTags.removeDuplicates(); - - return allTags; + emit tagFilterUpdated(); +} + +void VisualDeckStorageWidget::updateColorFilter() +{ + if (folderWidget) { + deckPreviewColorIdentityFilterWidget->filterWidgets(folderWidget->findChildren()); + } + emit colorFilterUpdated(); +} + +void VisualDeckStorageWidget::updateSearchFilter() +{ + if (folderWidget) { + searchWidget->filterWidgets(folderWidget->findChildren(), searchWidget->getSearchText()); + } + emit searchFilterUpdated(); } 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 f294a1790..563b269d0 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 @@ -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,40 +16,47 @@ class VisualDeckStorageSearchWidget; class VisualDeckStorageSortWidget; class VisualDeckStorageTagFilterWidget; +class VisualDeckStorageFolderDisplayWidget; class DeckPreviewColorIdentityFilterWidget; class VisualDeckStorageWidget final : public QWidget { Q_OBJECT 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 &allDecks); + void updateTagFilter(); + void updateColorFilter(); + void updateSearchFilter(); void updateSortOrder(); + void resizeEvent(QResizeEvent *event) override; signals: void bannerCardsRefreshed(); void deckPreviewClicked(QMouseEvent *event, DeckPreviewWidget *instance); void deckPreviewDoubleClicked(QMouseEvent *event, DeckPreviewWidget *instance); void deckLoadRequested(QString &filePath); + void tagFilterUpdated(); + void colorFilterUpdated(); + void searchFilterUpdated(); 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