From 595ac3fb0881cdc397f729fe5211ffa35cfb9791 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Fri, 9 Jul 2021 08:18:35 -0700 Subject: [PATCH] Project Manager: Feature tag filtering for header of the gem catalog (#1956) * [LYN-4440] Added new feature tag remove icon Signed-off-by: Benjamin Jillich * [LYN-4440] Added new gem filter tag widget Signed-off-by: Benjamin Jillich * [LYN-4440] Added feature tag widget used for showing the filtered tags in the gem catalog header Signed-off-by: Benjamin Jillich * [LYN-4440] Added filter tag widget container to the gem catalog header and connected it to the gem model Signed-off-by: Benjamin Jillich * [LYN-4440] Syncing the feature tag filters on the left panel with the tag widgets in the header and styling the item delegates Signed-off-by: Benjamin Jillich --- .../Resources/FeatureTagClose.svg | 4 + .../Resources/ProjectManager.qrc | 1 + .../Resources/ProjectManager.qss | 7 ++ .../Source/GemCatalog/GemFilterTagWidget.cpp | 96 +++++++++++++++++++ .../Source/GemCatalog/GemFilterTagWidget.h | 57 +++++++++++ .../Source/GemCatalog/GemFilterWidget.cpp | 11 +++ .../Source/GemCatalog/GemItemDelegate.cpp | 6 +- .../Source/GemCatalog/GemListHeaderWidget.cpp | 34 ++++++- .../Source/GemCatalog/GemListHeaderWidget.h | 3 +- .../project_manager_files.cmake | 2 + 10 files changed, 213 insertions(+), 8 deletions(-) create mode 100644 Code/Tools/ProjectManager/Resources/FeatureTagClose.svg create mode 100644 Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp create mode 100644 Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.h diff --git a/Code/Tools/ProjectManager/Resources/FeatureTagClose.svg b/Code/Tools/ProjectManager/Resources/FeatureTagClose.svg new file mode 100644 index 0000000000..9ab30acd65 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/FeatureTagClose.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index cfe4b37fbc..2e93e9eca9 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -34,5 +34,6 @@ Warning.svg Backgrounds/DefaultBackground.jpg Backgrounds/FtueBackground.jpg + FeatureTagClose.svg diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index d6a2476e83..0684ec1310 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -478,6 +478,13 @@ QProgressBar::chunk { background-color: #333333; } +/************** Filter tag widget **************/ + +#FilterTagWidgetTextLabel { + color: #94D2FF; + font-size: 10px; +} + /************** Gem Catalog (Inspector) **************/ #GemCatalogInspector { diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp new file mode 100644 index 0000000000..c4612debc8 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + FilterTagWidget::FilterTagWidget(const QString& text, QWidget* parent) + : QFrame(parent) + { + setFrameShape(QFrame::NoFrame); + + auto* layout = new QHBoxLayout(); + layout->setContentsMargins(6, 5, 4, 4); + layout->setSpacing(2); + setLayout(layout); + + setStyleSheet("background-color: #555555;"); + + m_textLabel = new QLabel(); + m_textLabel->setObjectName("FilterTagWidgetTextLabel"); + m_textLabel->setText(text); + layout->addWidget(m_textLabel); + + m_closeButton = new QPushButton(); + m_closeButton->setFlat(true); + m_closeButton->setIcon(QIcon(":/FeatureTagClose.svg")); + m_closeButton->setIconSize(QSize(12, 12)); + m_closeButton->setStyleSheet("QPushButton { background-color: transparent; border: 0px }"); + layout->addWidget(m_closeButton); + connect(m_closeButton, &QPushButton::clicked, this, [=]{ emit RemoveClicked(); }); + } + + QString FilterTagWidget::text() const + { + return m_textLabel->text(); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FilterTagWidgetContainer::FilterTagWidgetContainer(QWidget* parent) + : QWidget(parent) + { + m_layout = new QHBoxLayout(); + m_layout->setMargin(0); + m_layout->setSpacing(0); + setLayout(m_layout); + } + + void FilterTagWidgetContainer::Reinit(const QVector& tags) + { + if (m_widget) + { + // Hide the old widget and request deletion. + m_widget->hide(); + m_widget->deleteLater(); + } + + m_widget = new QWidget(this); + + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setAlignment(Qt::AlignLeft); + hLayout->setMargin(0); + hLayout->setSpacing(8); + + const int numTags = tags.count(); + for (int i = 0; i < numTags; ++i) + { + FilterTagWidget* tagWidget = new FilterTagWidget(tags[i]); + + // Add the tag widget to the current row. + hLayout->addWidget(tagWidget); + + // Connect the clicked event of the close button of the tag widget to the remove tag function in the container. + connect(tagWidget, &FilterTagWidget::RemoveClicked, this, [this, tagWidget]{ emit TagRemoved(tagWidget->text()); }); + } + + QWidget* spacerWidget = new QWidget(); + spacerWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + hLayout->addWidget(spacerWidget); + + m_widget->setLayout(hLayout); + m_layout->addWidget(m_widget); + + setFixedHeight(30); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.h new file mode 100644 index 0000000000..0994618d4e --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QHBoxLayout) +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QPushButton) +QT_FORWARD_DECLARE_CLASS(QWidget) + +namespace O3DE::ProjectManager +{ + class FilterTagWidget + : public QFrame + { + Q_OBJECT + + public: + FilterTagWidget(const QString& text, QWidget* parent = nullptr); + QString text() const; + + signals: + void RemoveClicked(); + + private: + QLabel* m_textLabel = nullptr; + QPushButton* m_closeButton = nullptr; + }; + + // Horizontally expanding filter tag container widget + class FilterTagWidgetContainer + : public QWidget + { + Q_OBJECT + + public: + FilterTagWidgetContainer(QWidget* parent = nullptr); + void Reinit(const QVector& tags); + + signals: + void TagRemoved(QString tagName); + + private: + QWidget* m_widget = nullptr; + QHBoxLayout* m_layout = nullptr; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp index 753371ba52..268e793ee8 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -485,6 +486,7 @@ namespace O3DE::ProjectManager const QString& feature = elementNames[i]; QAbstractButton* button = buttons[i]; + // Adjust the proxy model and enable or disable the clicked feature used for filtering. connect(button, &QAbstractButton::toggled, this, [=](bool checked) { QSet features = m_filterProxyModel->GetFeatures(); @@ -498,6 +500,15 @@ namespace O3DE::ProjectManager } m_filterProxyModel->SetFeatures(features); }); + + // Sync the UI state with the proxy model filtering. + connect(m_filterProxyModel, &GemSortFilterProxyModel::OnInvalidated, this, [=] + { + const QSet& filteredFeatureTags = m_filterProxyModel->GetFeatures(); + const bool isChecked = filteredFeatureTags.contains(button->text()); + QSignalBlocker signalsBlocker(button); + button->setChecked(isChecked); + }); } } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index b9dc3e9fcc..f5bedb79eb 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -102,9 +102,11 @@ namespace O3DE::ProjectManager // In case there are feature tags displayed at the bottom, decrease the size of the summary text field. const QStringList featureTags = GemModel::GetFeatures(modelIndex); - const int summaryHeight = contentRect.height() - (!featureTags.empty() * 30); + const int featureTagAreaHeight = 30; + const int summaryHeight = contentRect.height() - (!featureTags.empty() * featureTagAreaHeight); - const QSize summarySize = QSize(contentRect.width() - s_summaryStartX - s_buttonWidth - s_itemMargins.right() * 3, + const int additionalSummarySpacing = s_itemMargins.right() * 3; + const QSize summarySize = QSize(contentRect.width() - s_summaryStartX - s_buttonWidth - additionalSummarySpacing, summaryHeight); const QRect summaryRect = QRect(/*topLeft=*/QPoint(contentRect.left() + s_summaryStartX, contentRect.top()), summarySize); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp index 5e2f93c2fc..3d6fd1947c 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp @@ -7,10 +7,13 @@ #include #include -#include +#include +#include +#include #include -#include #include +#include +#include namespace O3DE::ProjectManager { @@ -23,11 +26,34 @@ namespace O3DE::ProjectManager setStyleSheet("background-color: #333333;"); - vLayout->addSpacing(20); + vLayout->addSpacing(13); // Top section QHBoxLayout* topLayout = new QHBoxLayout(); + topLayout->addSpacing(16); topLayout->setMargin(0); + + auto* tagWidget = new FilterTagWidgetContainer(); + + // Adjust the proxy model and disable the given feature used for filtering. + connect(tagWidget, &FilterTagWidgetContainer::TagRemoved, this, [=](QString tagName) + { + QSet filteredFeatureTags = proxyModel->GetFeatures(); + filteredFeatureTags.remove(tagName); + proxyModel->SetFeatures(filteredFeatureTags); + }); + + // Reinitialize the tag widget in case the filter in the proxy model got invalided. + connect(proxyModel, &GemSortFilterProxyModel::OnInvalidated, this, [=] + { + const QSet& tagSet = proxyModel->GetFeatures(); + QVector sortedTags(tagSet.begin(), tagSet.end()); + std::sort(sortedTags.begin(), sortedTags.end()); + tagWidget->Reinit(sortedTags); + }); + + topLayout->addWidget(tagWidget); + topLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); QLabel* showCountLabel = new QLabel(); @@ -43,7 +69,7 @@ namespace O3DE::ProjectManager vLayout->addLayout(topLayout); - vLayout->addSpacing(20); + vLayout->addSpacing(13); // Separating line QFrame* hLine = new QFrame(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.h index a9b3b3aa36..fcad58a321 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.h @@ -8,9 +8,8 @@ #pragma once #if !defined(Q_MOC_RUN) +#include #include -#include -#include #include #endif diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index a5632f082c..07c649624e 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -71,6 +71,8 @@ set(FILES Source/GemCatalog/GemCatalogHeaderWidget.cpp Source/GemCatalog/GemCatalogScreen.h Source/GemCatalog/GemCatalogScreen.cpp + Source/GemCatalog/GemFilterTagWidget.h + Source/GemCatalog/GemFilterTagWidget.cpp Source/GemCatalog/GemFilterWidget.h Source/GemCatalog/GemFilterWidget.cpp Source/GemCatalog/GemInfo.h