Explorar el Código

Adds Inspector to Gem Repo Screen (#4242)

* Adds the gem repo screen with the UI built but with mocked data and not connected to the o3de scripts

Signed-off-by: nggieber <[email protected]>

* Changed name of added to enabled, disabled define, removed unused functions

Signed-off-by: nggieber <[email protected]>

* Added Repo Screen Inspector UI

Signed-off-by: nggieber <[email protected]>

* Addressed minor PR feedback

Signed-off-by: nggieber <[email protected]>

* Add some more minor PR changes

Signed-off-by: nggieber <[email protected]>
AMZN-nggieber hace 3 años
padre
commit
9b14d49f87

+ 29 - 0
Code/Tools/ProjectManager/Resources/ProjectManager.qss

@@ -498,6 +498,18 @@ QProgressBar::chunk {
     font-size: 10px;
 }
 
+/************** Gems SubWidget **************/
+
+#gemSubWidgetTitleLabel {
+    color: #FFFFFF;
+    font-size: 16px;
+}
+
+#gemSubWidgetTextLabel {
+    color: #DDDDDD;
+    font-size: 10px;
+}
+
 /************** Gem Catalog (Inspector) **************/
 
 #GemCatalogInspector {
@@ -597,3 +609,20 @@ QProgressBar::chunk {
 #gemRepoInspector {
     background: #444444;
 }
+
+/************** Gem Repo Inspector **************/
+
+#gemRepoInspectorNameLabel {
+    font-size: 18px;
+    color: #FFFFFF;
+}
+
+#gemRepoInspectorBodyLabel {
+    font-size: 12px;
+    color: #DDDDDD;
+}
+
+#gemRepoInspectorAddInfoTitleLabel {
+    font-size: 16px;
+    color: #FFFFFF;
+}

+ 2 - 24
Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp

@@ -8,6 +8,7 @@
 
 #include <GemCatalog/GemInspector.h>
 #include <GemCatalog/GemItemDelegate.h>
+
 #include <QFrame>
 #include <QLabel>
 #include <QSpacerItem>
@@ -89,7 +90,7 @@ namespace O3DE::ProjectManager
         // Additional information
         m_versionLabel->setText(QString("Gem Version: %1").arg(m_model->GetVersion(modelIndex)));
         m_lastUpdatedLabel->setText(QString("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex)));
-        m_binarySizeLabel->setText(QString("Binary Size:  %1 KB").arg(QString::number(m_model->GetBinarySizeInKB(modelIndex))));
+        m_binarySizeLabel->setText(QString("Binary Size:  %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex)));
 
         m_mainWidget->adjustSize();
         m_mainWidget->show();
@@ -185,27 +186,4 @@ namespace O3DE::ProjectManager
         m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
         m_binarySizeLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
     }
-
-    GemInspector::GemsSubWidget::GemsSubWidget(QWidget* parent)
-        : QWidget(parent)
-    {
-        m_layout = new QVBoxLayout();
-        m_layout->setAlignment(Qt::AlignTop);
-        m_layout->setMargin(0);
-        setLayout(m_layout);
-
-        m_titleLabel = GemInspector::CreateStyledLabel(m_layout, 16, s_headerColor);
-        m_textLabel = GemInspector::CreateStyledLabel(m_layout, 10, s_textColor);
-        m_textLabel->setWordWrap(true);
-
-        m_tagWidget = new TagContainerWidget();
-        m_layout->addWidget(m_tagWidget);
-    }
-
-    void GemInspector::GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames)
-    {
-        m_titleLabel->setText(title);
-        m_textLabel->setText(text);
-        m_tagWidget->Update(gemNames);
-    }
 } // namespace O3DE::ProjectManager

+ 3 - 17
Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h

@@ -9,10 +9,11 @@
 #pragma once
 
 #if !defined(Q_MOC_RUN)
-#include <LinkWidget.h>
-#include <TagWidget.h>
 #include <GemCatalog/GemInfo.h>
 #include <GemCatalog/GemModel.h>
+#include <GemsSubWidget.h>
+#include <LinkWidget.h>
+
 #include <QItemSelection>
 #include <QScrollArea>
 #include <QWidget>
@@ -43,21 +44,6 @@ namespace O3DE::ProjectManager
         void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
 
     private:
-        // Title, description and tag widget container used for the depending and conflicting gems
-        class GemsSubWidget
-            : public QWidget
-        {
-        public:
-            GemsSubWidget(QWidget* parent = nullptr);
-            void Update(const QString& title, const QString& text, const QStringList& gemNames);
-
-        private:
-            QLabel* m_titleLabel = nullptr;
-            QLabel* m_textLabel = nullptr;
-            QVBoxLayout* m_layout = nullptr;
-            TagContainerWidget* m_tagWidget = nullptr;
-        };
-
         void InitMainWidget();
 
         GemModel* m_model = nullptr;

+ 4 - 2
Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.cpp

@@ -11,10 +11,12 @@
 namespace O3DE::ProjectManager
 {
     GemRepoInfo::GemRepoInfo(
-        const QString& name, const QString& creator, const QString& summary, const QDateTime& lastUpdated, bool isEnabled = true)
+        const QString& name,
+        const QString& creator,
+        const QDateTime& lastUpdated,
+        bool isEnabled = true)
         : m_name(name)
         , m_creator(creator)
-        , m_summary(summary)
         , m_lastUpdated(lastUpdated)
         , m_isEnabled(isEnabled)
     {

+ 10 - 4
Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h

@@ -19,19 +19,25 @@ namespace O3DE::ProjectManager
     {
     public:
         GemRepoInfo() = default;
-        GemRepoInfo(const QString& name, const QString& creator, const QString& summary, const QDateTime& lastUpdated, bool isEnabled);
+        GemRepoInfo(
+            const QString& name,
+            const QString& creator,
+            const QDateTime& lastUpdated,
+            bool isEnabled);
 
         bool IsValid() const;
 
         bool operator<(const GemRepoInfo& gemRepoInfo) const;
 
-        QString m_path;
+        QString m_path = "";
         QString m_name = "Unknown Gem Repo Name";
         QString m_creator = "Unknown Creator";
         bool m_isEnabled = false; //! Is the repo currently enabled for this engine?
         QString m_summary = "No summary provided.";
-        QString m_directoryLink;
-        QString m_repoLink;
+        QString m_additionalInfo = "";
+        QString m_directoryLink = "";
+        QString m_repoLink = "";
+        QStringList m_includedGemPaths = {};
         QDateTime m_lastUpdated;
     };
 } // namespace O3DE::ProjectManager

+ 144 - 0
Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp

@@ -0,0 +1,144 @@
+/*
+ * 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 <GemRepo/GemRepoInspector.h>
+#include <GemRepo/GemRepoItemDelegate.h>
+
+#include <QFrame>
+#include <QLabel>
+#include <QVBoxLayout>
+#include <QIcon>
+
+namespace O3DE::ProjectManager
+{
+    GemRepoInspector::GemRepoInspector(GemRepoModel* model, QWidget* parent)
+        : QScrollArea(parent)
+        , m_model(model)
+    {
+        setObjectName("gemRepoInspector");
+        setWidgetResizable(true);
+        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+        setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+
+        m_mainWidget = new QWidget();
+        setWidget(m_mainWidget);
+
+        m_mainLayout = new QVBoxLayout();
+        m_mainLayout->setMargin(15);
+        m_mainLayout->setAlignment(Qt::AlignTop);
+        m_mainWidget->setLayout(m_mainLayout);
+
+        InitMainWidget();
+
+        connect(m_model->GetSelectionModel(), &QItemSelectionModel::selectionChanged, this, &GemRepoInspector::OnSelectionChanged);
+        Update({});
+    }
+
+    void GemRepoInspector::OnSelectionChanged(const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected)
+    {
+        const QModelIndexList selectedIndices = selected.indexes();
+        if (selectedIndices.empty())
+        {
+            Update({});
+            return;
+        }
+
+        Update(selectedIndices[0]);
+    }
+
+    void GemRepoInspector::Update(const QModelIndex& modelIndex)
+    {
+        if (!modelIndex.isValid())
+        {
+            m_mainWidget->hide();
+        }
+
+        // Repo name and url link
+        m_nameLabel->setText(m_model->GetName(modelIndex));
+        m_repoLinkLabel->setText(m_model->GetRepoLink(modelIndex));
+        m_repoLinkLabel->SetUrl(m_model->GetRepoLink(modelIndex));
+
+        // Repo summary
+        m_summaryLabel->setText(m_model->GetSummary(modelIndex));
+        m_summaryLabel->adjustSize();
+
+        // Additional information
+        if (m_model->HasAdditionalInfo(modelIndex))
+        {
+            m_addInfoTitleLabel->show();
+            m_addInfoTextLabel->show();
+
+            m_addInfoSpacer->changeSize(0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+            m_addInfoTextLabel->setText(m_model->GetAdditionalInfo(modelIndex));
+        }
+        else
+        {
+            m_addInfoTitleLabel->hide();
+            m_addInfoTextLabel->hide();
+
+            m_addInfoSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
+        }
+
+        // Included Gems
+        m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemNames(modelIndex));
+
+        m_mainWidget->adjustSize();
+        m_mainWidget->show();
+    }
+
+    void GemRepoInspector::InitMainWidget()
+    {
+        // Repo name and url link
+        m_nameLabel = new QLabel();
+        m_nameLabel->setObjectName("gemRepoInspectorNameLabel");
+        m_mainLayout->addWidget(m_nameLabel);
+
+        m_repoLinkLabel = new LinkLabel(tr("Repo Url"), QUrl(""), 12, this);
+        m_mainLayout->addWidget(m_repoLinkLabel);
+        m_mainLayout->addSpacing(5);
+
+        // Repo summary
+        m_summaryLabel = new QLabel();
+        m_summaryLabel->setObjectName("gemRepoInspectorBodyLabel");
+        m_summaryLabel->setWordWrap(true);
+        m_summaryLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
+        m_summaryLabel->setOpenExternalLinks(true);
+        m_mainLayout->addWidget(m_summaryLabel);
+        m_mainLayout->addSpacing(20);
+
+        // Separating line
+        QFrame* hLine = new QFrame();
+        hLine->setFrameShape(QFrame::HLine);
+        hLine->setObjectName("horizontalSeparatingLine");
+        m_mainLayout->addWidget(hLine);
+        m_mainLayout->addSpacing(10);
+
+        // Additional information
+        m_addInfoTitleLabel = new QLabel();
+        m_addInfoTitleLabel->setObjectName("gemRepoInspectorAddInfoTitleLabel");
+        m_addInfoTitleLabel->setText(tr("Additional Information"));
+        m_mainLayout->addWidget(m_addInfoTitleLabel);
+
+        m_addInfoTextLabel = new QLabel();
+        m_addInfoTextLabel->setObjectName("gemRepoInspectorBodyLabel");
+        m_addInfoTextLabel->setWordWrap(true);
+        m_addInfoTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
+        m_addInfoTextLabel->setOpenExternalLinks(true);
+        m_mainLayout->addWidget(m_addInfoTextLabel);
+
+        // Conditional spacing for additional info section
+        m_addInfoSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding);
+        m_mainLayout->addSpacerItem(m_addInfoSpacer);
+
+        // Included Gems
+        m_includedGems = new GemsSubWidget();
+        m_mainLayout->addWidget(m_includedGems);
+        m_mainLayout->addSpacing(20);
+    }
+} // namespace O3DE::ProjectManager

+ 59 - 0
Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.h

@@ -0,0 +1,59 @@
+/*
+ * 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 <GemRepo/GemRepoModel.h>
+#include <LinkWidget.h>
+#include <GemsSubWidget.h>
+
+#include <QItemSelection>
+#include <QScrollArea>
+#include <QSpacerItem>
+#include <QWidget>
+#endif
+
+QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
+QT_FORWARD_DECLARE_CLASS(QLabel)
+
+namespace O3DE::ProjectManager
+{
+    class GemRepoInspector : public QScrollArea
+    {
+        Q_OBJECT // AUTOMOC
+
+            public : explicit GemRepoInspector(GemRepoModel* model, QWidget* parent = nullptr);
+        ~GemRepoInspector() = default;
+
+        void Update(const QModelIndex& modelIndex);
+
+    private slots:
+        void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
+
+    private:
+        void InitMainWidget();
+
+        GemRepoModel* m_model = nullptr;
+        QWidget* m_mainWidget = nullptr;
+        QVBoxLayout* m_mainLayout = nullptr;
+
+        // General info section
+        QLabel* m_nameLabel = nullptr;
+        LinkLabel* m_repoLinkLabel = nullptr;
+        QLabel* m_summaryLabel = nullptr;
+
+        // Additional information
+        QLabel* m_addInfoTitleLabel = nullptr;
+        QLabel* m_addInfoTextLabel = nullptr;
+        QSpacerItem* m_addInfoSpacer = nullptr;
+
+        // Included Gems
+        GemsSubWidget* m_includedGems = nullptr;
+    };
+} // namespace O3DE::ProjectManager

+ 2 - 1
Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp

@@ -11,13 +11,14 @@
 
 namespace O3DE::ProjectManager
 {
-    GemRepoListView::GemRepoListView(QAbstractItemModel* model, QWidget* parent)
+    GemRepoListView::GemRepoListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent)
         : QListView(parent)
     {
         setObjectName("gemRepoListView");
         setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
 
         setModel(model);
+        setSelectionModel(selectionModel);
         setItemDelegate(new GemRepoItemDelegate(model, this));
     }
 } // namespace O3DE::ProjectManager

+ 2 - 1
Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h

@@ -10,6 +10,7 @@
 
 #if !defined(Q_MOC_RUN)
 #include <QListView>
+#include <QItemSelectionModel>
 #endif
 
 QT_FORWARD_DECLARE_CLASS(QAbstractItemModel)
@@ -22,7 +23,7 @@ namespace O3DE::ProjectManager
         Q_OBJECT // AUTOMOC
 
     public:
-        explicit GemRepoListView(QAbstractItemModel* model, QWidget* parent = nullptr);
+        explicit GemRepoListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent = nullptr);
         ~GemRepoListView() = default;
     };
 } // namespace O3DE::ProjectManager

+ 61 - 0
Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp

@@ -7,8 +7,10 @@
  */
 
 #include <GemRepo/GemRepoModel.h>
+#include <PythonBindings.h>
 
 #include <QItemSelectionModel>
+#include <QMessageBox>
 
 namespace O3DE::ProjectManager
 {
@@ -16,6 +18,7 @@ namespace O3DE::ProjectManager
         : QStandardItemModel(parent)
     {
         m_selectionModel = new QItemSelectionModel(this, parent);
+        m_gemModel = new GemModel(this);
     }
 
     QItemSelectionModel* GemRepoModel::GetSelectionModel() const
@@ -37,8 +40,17 @@ namespace O3DE::ProjectManager
         item->setData(gemRepoInfo.m_repoLink, RoleRepoLink);
         item->setData(gemRepoInfo.m_lastUpdated, RoleLastUpdated);
         item->setData(gemRepoInfo.m_path, RolePath);
+        item->setData(gemRepoInfo.m_additionalInfo, RoleAdditionalInfo);
+        item->setData(gemRepoInfo.m_includedGemPaths, RoleIncludedGems);
 
         appendRow(item);
+
+        QVector<GemInfo> includedGemInfos = GetIncludedGemInfos(item->index());
+
+        for (const GemInfo& gemInfo : includedGemInfos)
+        {
+            m_gemModel->AddGem(gemInfo);
+        }
     }
 
     void GemRepoModel::Clear()
@@ -61,6 +73,11 @@ namespace O3DE::ProjectManager
         return modelIndex.data(RoleSummary).toString();
     }
 
+    QString GemRepoModel::GetAdditionalInfo(const QModelIndex& modelIndex)
+    {
+        return modelIndex.data(RoleAdditionalInfo).toString();
+    }
+
     QString GemRepoModel::GetDirectoryLink(const QModelIndex& modelIndex)
     {
         return modelIndex.data(RoleDirectoryLink).toString();
@@ -81,6 +98,45 @@ namespace O3DE::ProjectManager
         return modelIndex.data(RolePath).toString();
     }
 
+    QStringList GemRepoModel::GetIncludedGemPaths(const QModelIndex& modelIndex)
+    {
+        return modelIndex.data(RoleIncludedGems).toStringList();
+    }
+
+    QStringList GemRepoModel::GetIncludedGemNames(const QModelIndex& modelIndex)
+    {
+        QStringList gemNames;
+        QVector<GemInfo> gemInfos = GetIncludedGemInfos(modelIndex);
+
+        for (const GemInfo& gemInfo : gemInfos)
+        {
+            gemNames.append(gemInfo.m_displayName);
+        }
+
+        return gemNames;
+    }
+
+    QVector<GemInfo> GemRepoModel::GetIncludedGemInfos(const QModelIndex& modelIndex)
+    {
+        QVector<GemInfo> allGemInfos;
+        QStringList repoGemPaths = GetIncludedGemPaths(modelIndex);
+
+        for (const QString& gemPath : repoGemPaths)
+        {
+            AZ::Outcome<GemInfo> gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(gemPath);
+            if (gemInfoResult.IsSuccess())
+            {
+                allGemInfos.append(gemInfoResult.GetValue());
+            }
+            else
+            {
+                QMessageBox::critical(nullptr, tr("Gem Not Found"), tr("Cannot find info for gem %1.").arg(gemPath));
+            }
+        }
+
+        return allGemInfos;
+    }
+
     bool GemRepoModel::IsEnabled(const QModelIndex& modelIndex)
     {
         return modelIndex.data(RoleIsEnabled).toBool();
@@ -91,4 +147,9 @@ namespace O3DE::ProjectManager
         model.setData(modelIndex, isEnabled, RoleIsEnabled);
     }
 
+    bool GemRepoModel::HasAdditionalInfo(const QModelIndex& modelIndex)
+    {
+        return !modelIndex.data(RoleAdditionalInfo).toString().isEmpty();
+    }
+
 } // namespace O3DE::ProjectManager

+ 12 - 1
Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h

@@ -11,6 +11,7 @@
 #if !defined(Q_MOC_RUN)
 #include <QStandardItemModel>
 #include <GemRepo/GemRepoInfo.h>
+#include <GemCatalog/GemModel.h>
 #endif
 
 QT_FORWARD_DECLARE_CLASS(QItemSelectionModel)
@@ -32,13 +33,19 @@ namespace O3DE::ProjectManager
         static QString GetName(const QModelIndex& modelIndex);
         static QString GetCreator(const QModelIndex& modelIndex);
         static QString GetSummary(const QModelIndex& modelIndex);
+        static QString GetAdditionalInfo(const QModelIndex& modelIndex);
         static QString GetDirectoryLink(const QModelIndex& modelIndex);
         static QString GetRepoLink(const QModelIndex& modelIndex);
         static QDateTime GetLastUpdated(const QModelIndex& modelIndex);
         static QString GetPath(const QModelIndex& modelIndex);
 
+        static QStringList GetIncludedGemPaths(const QModelIndex& modelIndex);
+        static QStringList GetIncludedGemNames(const QModelIndex& modelIndex);
+        static QVector<GemInfo> GetIncludedGemInfos(const QModelIndex& modelIndex);
+
         static bool IsEnabled(const QModelIndex& modelIndex);
         static void SetEnabled(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isEnabled);
+        static bool HasAdditionalInfo(const QModelIndex& modelIndex);
 
     private:
         enum UserRole
@@ -50,9 +57,13 @@ namespace O3DE::ProjectManager
             RoleDirectoryLink,
             RoleRepoLink,
             RoleLastUpdated,
-            RolePath
+            RolePath,
+            RoleAdditionalInfo,
+            RoleIncludedGems,
         };
 
         QItemSelectionModel* m_selectionModel = nullptr;
+
+        GemModel* m_gemModel = nullptr;
     };
 } // namespace O3DE::ProjectManager

+ 6 - 6
Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp

@@ -10,6 +10,7 @@
 #include <GemRepo/GemRepoItemDelegate.h>
 #include <GemRepo/GemRepoListView.h>
 #include <GemRepo/GemRepoModel.h>
+#include <GemRepo/GemRepoInspector.h>
 #include <PythonBindingsInterface.h>
 
 #include <QVBoxLayout>
@@ -40,10 +41,6 @@ namespace O3DE::ProjectManager
 
         hLayout->addSpacing(60);
 
-        m_gemRepoInspector = new QFrame(this);
-        m_gemRepoInspector->setObjectName(tr("gemRepoInspector"));
-        m_gemRepoInspector->setFixedWidth(240);
-
         QVBoxLayout* middleVLayout = new QVBoxLayout();
         middleVLayout->setMargin(0);
         middleVLayout->setSpacing(0);
@@ -99,10 +96,13 @@ namespace O3DE::ProjectManager
         m_gemRepoHeaderTable->horizontalHeader()->setStyleSheet("QHeaderView::section { background-color:transparent; color:white; font-size:12px; text-align:left; border-style:none; }");
         middleVLayout->addWidget(m_gemRepoHeaderTable);
 
-        m_gemRepoListView = new GemRepoListView(m_gemRepoModel, this);
+        m_gemRepoListView = new GemRepoListView(m_gemRepoModel, m_gemRepoModel->GetSelectionModel(), this);
         middleVLayout->addWidget(m_gemRepoListView);
 
         hLayout->addLayout(middleVLayout);
+
+        m_gemRepoInspector = new GemRepoInspector(m_gemRepoModel, this);
+        m_gemRepoInspector->setFixedWidth(240);
         hLayout->addWidget(m_gemRepoInspector);
 
         Reinit();
@@ -134,7 +134,7 @@ namespace O3DE::ProjectManager
         }
         else
         {
-            QMessageBox::critical(this, tr("Operation failed"), QString("Cannot retrieve gem repos for engine.\n\nError:\n%2").arg(allGemRepoInfosResult.GetError().c_str()));
+            QMessageBox::critical(this, tr("Operation failed"), tr("Cannot retrieve gem repos for engine.\n\nError:\n%2").arg(allGemRepoInfosResult.GetError().c_str()));
         }
     }
 

+ 2 - 1
Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h

@@ -19,6 +19,7 @@ QT_FORWARD_DECLARE_CLASS(QTableWidget)
 
 namespace O3DE::ProjectManager
 {
+    QT_FORWARD_DECLARE_CLASS(GemRepoInspector)
     QT_FORWARD_DECLARE_CLASS(GemRepoListView)
     QT_FORWARD_DECLARE_CLASS(GemRepoModel)
 
@@ -40,7 +41,7 @@ namespace O3DE::ProjectManager
         QTableWidget* m_gemRepoHeaderTable = nullptr;
         QHeaderView* m_gemRepoListHeader = nullptr;
         GemRepoListView* m_gemRepoListView = nullptr;
-        QFrame* m_gemRepoInspector = nullptr;
+        GemRepoInspector* m_gemRepoInspector = nullptr;
         GemRepoModel* m_gemRepoModel = nullptr;
 
         QLabel* m_lastAllUpdateLabel;

+ 45 - 0
Code/Tools/ProjectManager/Source/GemsSubWidget.cpp

@@ -0,0 +1,45 @@
+/*
+ * 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 <GemsSubWidget.h>
+#include <TagWidget.h>
+
+#include <QFrame>
+#include <QLabel>
+#include <QVBoxLayout>
+
+namespace O3DE::ProjectManager
+{
+    GemsSubWidget::GemsSubWidget(QWidget* parent)
+        : QWidget(parent)
+    {
+        m_layout = new QVBoxLayout();
+        m_layout->setAlignment(Qt::AlignTop);
+        m_layout->setMargin(0);
+        setLayout(m_layout);
+
+        m_titleLabel = new QLabel();
+        m_titleLabel->setObjectName("gemSubWidgetTitleLabel");
+        m_layout->addWidget(m_titleLabel);
+
+        m_textLabel = new QLabel();
+        m_textLabel->setObjectName("gemSubWidgetTextLabel");
+        m_textLabel->setWordWrap(true);
+        m_layout->addWidget(m_textLabel);
+
+        m_tagWidget = new TagContainerWidget();
+        m_layout->addWidget(m_tagWidget);
+    }
+
+    void GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames)
+    {
+        m_titleLabel->setText(title);
+        m_textLabel->setText(text);
+        m_tagWidget->Update(gemNames);
+    }
+} // namespace O3DE::ProjectManager

+ 35 - 0
Code/Tools/ProjectManager/Source/GemsSubWidget.h

@@ -0,0 +1,35 @@
+/*
+ * 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 <QWidget>
+#include <TagWidget.h>
+#endif
+
+QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
+QT_FORWARD_DECLARE_CLASS(QLabel)
+
+namespace O3DE::ProjectManager
+{
+    // Title, description and tag widget container used for the depending and conflicting gems
+    class GemsSubWidget
+        : public QWidget
+    {
+    public:
+        GemsSubWidget(QWidget* parent = nullptr);
+        void Update(const QString& title, const QString& text, const QStringList& gemNames);
+
+    private:
+        QLabel* m_titleLabel = nullptr;
+        QLabel* m_textLabel = nullptr;
+        QVBoxLayout* m_layout = nullptr;
+        TagContainerWidget* m_tagWidget = nullptr;
+    };
+} // namespace O3DE::ProjectManager

+ 4 - 3
Code/Tools/ProjectManager/Source/LinkWidget.cpp

@@ -14,9 +14,10 @@
 
 namespace O3DE::ProjectManager
 {
-    LinkLabel::LinkLabel(const QString& text, const QUrl& url, QWidget* parent)
+    LinkLabel::LinkLabel(const QString& text, const QUrl& url, int fontSize, QWidget* parent)
         : QLabel(text, parent)
         , m_url(url)
+        , m_fontSize(fontSize)
     {
         SetDefaultStyle();
     }
@@ -33,7 +34,7 @@ namespace O3DE::ProjectManager
 
     void LinkLabel::enterEvent([[maybe_unused]] QEvent* event)
     {
-        setStyleSheet("font-size: 10px; color: #94D2FF; text-decoration: underline;");
+        setStyleSheet(QString("font-size: %1px; color: #94D2FF; text-decoration: underline;").arg(m_fontSize));
     }
 
     void LinkLabel::leaveEvent([[maybe_unused]] QEvent* event)
@@ -48,6 +49,6 @@ namespace O3DE::ProjectManager
 
     void LinkLabel::SetDefaultStyle()
     {
-        setStyleSheet("font-size: 10px; color: #94D2FF;");
+        setStyleSheet(QString("font-size: %1px; color: #94D2FF;").arg(m_fontSize));
     }
 } // namespace O3DE::ProjectManager

+ 2 - 1
Code/Tools/ProjectManager/Source/LinkWidget.h

@@ -25,7 +25,7 @@ namespace O3DE::ProjectManager
         Q_OBJECT // AUTOMOC
 
     public:
-        LinkLabel(const QString& text = {}, const QUrl& url = {}, QWidget* parent = nullptr);
+        LinkLabel(const QString& text = {}, const QUrl& url = {}, int fontSize = 10, QWidget* parent = nullptr);
 
         void SetUrl(const QUrl& url);
 
@@ -40,5 +40,6 @@ namespace O3DE::ProjectManager
 
     private:
         QUrl m_url;
+        int m_fontSize;
     };
 } // namespace O3DE::ProjectManager

+ 10 - 2
Code/Tools/ProjectManager/Source/PythonBindings.cpp

@@ -954,8 +954,16 @@ namespace O3DE::ProjectManager
             return AZ::Failure<AZStd::string>(result.GetError().c_str());
         }
 #else
-        gemRepos.push_back(GemRepoInfo("JohnCreates", "John Smith", "", QDateTime(QDate(2021, 8, 31), QTime(11, 57)), true));
-        gemRepos.push_back(GemRepoInfo("JanesGems", "Jane Doe", "", QDateTime(QDate(2021, 9, 10), QTime(18, 23)), false));
+        GemRepoInfo mockJohnRepo("JohnCreates", "John Smith", QDateTime(QDate(2021, 8, 31), QTime(11, 57)), true);
+        mockJohnRepo.m_summary = "John's Summary. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sollicitudin dapibus urna";
+        mockJohnRepo.m_repoLink = "https://github.com/o3de/o3de";
+        mockJohnRepo.m_additionalInfo = "John's additional info. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sollicitu.";
+        gemRepos.push_back(mockJohnRepo);
+
+        GemRepoInfo mockJaneRepo("JanesGems", "Jane Doe", QDateTime(QDate(2021, 9, 10), QTime(18, 23)), false);
+        mockJaneRepo.m_summary = "Jane's Summary.";
+        mockJaneRepo.m_repoLink = "https://github.com/o3de/o3de.org";
+        gemRepos.push_back(mockJaneRepo);
 #endif // MOCK_GEM_REPO_INFO
 
         std::sort(gemRepos.begin(), gemRepos.end());

+ 3 - 2
Code/Tools/ProjectManager/Source/PythonBindingsInterface.h

@@ -56,8 +56,9 @@ namespace O3DE::ProjectManager
         // Gems
 
         /**
-         * Get info about a Gem 
-         * @param projectPath the absolute path to the Gem 
+         * Get info about a Gem.
+         * @param path The absolute path to the Gem
+         * @param projectPath (Optional) The absolute path to the Gem project
          * @return an outcome with GemInfo on success 
          */
         virtual AZ::Outcome<GemInfo> GetGemInfo(const QString& path, const QString& projectPath = {}) = 0;

+ 4 - 0
Code/Tools/ProjectManager/project_manager_files.cmake

@@ -27,6 +27,8 @@ set(FILES
     Source/FormFolderBrowseEditWidget.cpp
     Source/FormImageBrowseEditWidget.h
     Source/FormImageBrowseEditWidget.cpp
+    Source/GemsSubWidget.h
+    Source/GemsSubWidget.cpp
     Source/PathValidator.h
     Source/PathValidator.cpp
     Source/ProjectManagerWindow.h
@@ -104,6 +106,8 @@ set(FILES
     Source/GemRepo/GemRepoScreen.cpp
     Source/GemRepo/GemRepoInfo.h
     Source/GemRepo/GemRepoInfo.cpp
+    Source/GemRepo/GemRepoInspector.h
+    Source/GemRepo/GemRepoInspector.cpp
     Source/GemRepo/GemRepoItemDelegate.h
     Source/GemRepo/GemRepoItemDelegate.cpp
     Source/GemRepo/GemRepoListView.h