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