2
0
Эх сурвалжийг харах

Merge pull request #11295 from aws-lumberyard-dev/Prism/RemoteTemplatesDownload

Making remote templates visible
AMZN-Phil 3 жил өмнө
parent
commit
31d41540f5

+ 21 - 0
Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp

@@ -19,6 +19,7 @@
 #include <ProjectUtils.h>
 
 #include <AzCore/Math/Uuid.h>
+#include <AzCore/std/ranges/ranges_algorithm.h>
 #include <AzQtComponents/Components/FlowLayout.h>
 
 #include <QVBoxLayout>
@@ -130,6 +131,25 @@ namespace O3DE::ProjectManager
         {
             m_templates = templatesResult.GetValue();
 
+            // Add in remote templates
+            auto remoteTemplatesResult = PythonBindingsInterface::Get()->GetProjectTemplatesForAllRepos();
+            if (remoteTemplatesResult.IsSuccess() && !remoteTemplatesResult.GetValue().isEmpty())
+            {
+                const QVector<ProjectTemplateInfo>& remoteTemplates = remoteTemplatesResult.GetValue();
+                for (const ProjectTemplateInfo& remoteTemplate : remoteTemplates)
+                {
+                    const auto found = AZStd::ranges::find_if(m_templates,
+                        [remoteTemplate](const ProjectTemplateInfo& value)
+                        {
+                            return remoteTemplate.m_name == value.m_name;
+                        });
+                    if (found == m_templates.end())
+                    {
+                        m_templates.append(remoteTemplate);
+                    }
+                }
+            }
+
             // sort alphabetically by display name (but putting Standard first) because they could be in any order
             std::sort(m_templates.begin(), m_templates.end(), [](const ProjectTemplateInfo& arg1, const ProjectTemplateInfo& arg2)
             {
@@ -157,6 +177,7 @@ namespace O3DE::ProjectManager
                     projectPreviewPath = ":/DefaultTemplate.png";
                 }
                 TemplateButton* templateButton = new TemplateButton(projectPreviewPath, projectTemplate.m_displayName, this);
+                templateButton->SetIsRemote(projectTemplate.m_isRemote);
                 templateButton->setCheckable(true);
                 templateButton->setProperty(k_templateIndexProperty, index);
                 

+ 1 - 0
Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h

@@ -30,5 +30,6 @@ namespace O3DE::ProjectManager
         QStringList m_includedGems;
         QStringList m_canonicalTags;
         QStringList m_userTags;
+        bool m_isRemote = false;
     };
 } // namespace O3DE::ProjectManager

+ 30 - 0
Code/Tools/ProjectManager/Source/PythonBindings.cpp

@@ -1210,6 +1210,36 @@ namespace O3DE::ProjectManager
         }
     }
 
+    AZ::Outcome<QVector<ProjectTemplateInfo>> PythonBindings::GetProjectTemplatesForAllRepos(const QString& projectPath)
+    {
+        QVector<ProjectTemplateInfo> templates;
+
+        bool result = ExecuteWithLock(
+            [&]
+            {
+                auto templatePaths = m_repo.attr("get_template_json_paths_from_all_cached_repos")();
+
+                if (pybind11::isinstance<pybind11::set>(templatePaths))
+                {
+                    for (auto path : templatePaths)
+                    {
+                        ProjectTemplateInfo remoteTemplate = ProjectTemplateInfoFromPath(path, QString_To_Py_Path(projectPath));
+                        remoteTemplate.m_isRemote = true;
+                        templates.push_back(remoteTemplate);
+                    }
+                }
+            });
+
+        if (!result)
+        {
+            return AZ::Failure();
+        }
+        else
+        {
+            return AZ::Success(AZStd::move(templates));
+        }
+    }
+
     AZ::Outcome<void, AZStd::string> PythonBindings::RefreshGemRepo(const QString& repoUri)
     {
         bool refreshResult = false;

+ 1 - 0
Code/Tools/ProjectManager/Source/PythonBindings.h

@@ -66,6 +66,7 @@ namespace O3DE::ProjectManager
 
         // ProjectTemplate
         AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplates(const QString& projectPath = {}) override;
+        AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplatesForAllRepos(const QString& projectPath = {}) override;
 
         // Gem Repos
         AZ::Outcome<void, AZStd::string> RefreshGemRepo(const QString& repoUri) override;

+ 6 - 0
Code/Tools/ProjectManager/Source/PythonBindingsInterface.h

@@ -232,6 +232,12 @@ namespace O3DE::ProjectManager
          */
         virtual AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplates(const QString& projectPath = {}) = 0;
 
+        /**
+         * Gathers all project templates for all templates registered from repos.
+         * @return A list of all ProjectTemplateInfos on success
+         */
+        virtual AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplatesForAllRepos(const QString& projectPath = {}) = 0;
+
         // Gem Repos
 
         /**

+ 87 - 0
Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp

@@ -9,10 +9,13 @@
 #include <TemplateButtonWidget.h>
 #include <ProjectManagerDefs.h>
 
+#include <QHBoxLayout>
 #include <QVBoxLayout>
 #include <QLabel>
 #include <QPixmap>
 #include <QAbstractButton>
+#include <QProgressBar>
+#include <QSpacerItem>
 #include <QStyle>
 #include <QVariant>
 
@@ -41,9 +44,93 @@ namespace O3DE::ProjectManager
         label->setWordWrap(true);
         vLayout->addWidget(label);
 
+        // Create an overlay for remote templates
+        QGridLayout* overlayLayout = new QGridLayout();
+        overlayLayout->setAlignment(Qt::AlignTop);
+        overlayLayout->setSpacing(0);
+        overlayLayout->setContentsMargins(0, 0, 0, 0);
+
+        // Dark overlay to make text clearer
+        m_darkenOverlay = new QLabel(this);
+        m_darkenOverlay->setObjectName("labelButtonOverlay");
+        m_darkenOverlay->setFixedSize(ProjectTemplateImageWidth, ProjectTemplateImageWidth);
+        m_darkenOverlay->setVisible(false);
+        overlayLayout->addWidget(m_darkenOverlay,0,0);
+
+        QVBoxLayout* contentsLayout = new QVBoxLayout();
+        contentsLayout->setSpacing(0);
+        contentsLayout->setContentsMargins(0, 0, 0, 0);
+        contentsLayout->setAlignment(Qt::AlignTop);
+        overlayLayout->addLayout(contentsLayout, 0, 0);
+
+        // Top status icons
+        QHBoxLayout* statusIconsLayout = new QHBoxLayout();
+        statusIconsLayout->setSpacing(0);
+        statusIconsLayout->setContentsMargins(0, 0, 0, 0);
+        statusIconsLayout->addStretch();
+        m_cloudIcon = new QLabel(this);
+        m_cloudIcon->setObjectName("projectCloudIconOverlay");
+        m_cloudIcon->setPixmap(QIcon(":/Download.svg").pixmap(24, 24));
+        m_cloudIcon->setVisible(false);
+        statusIconsLayout->addWidget(m_cloudIcon);
+        statusIconsLayout->addSpacing(5);
+        image->setLayout(overlayLayout);
+
+        QWidget* statusIconArea = new QWidget();
+        statusIconArea->setFixedSize(ProjectTemplateImageWidth, 24);
+        statusIconArea->setLayout(statusIconsLayout);
+        contentsLayout->addWidget(statusIconArea);
+
+        QWidget* templateCenter = new QWidget();
+        templateCenter->setFixedSize(ProjectTemplateImageWidth, 35);
+
+        // Center area for download information
+        QVBoxLayout* centerBlock = new QVBoxLayout();
+        templateCenter->setLayout(centerBlock);
+
+        QHBoxLayout* downloadProgressTextBlock = new QHBoxLayout();
+        m_progressMessageLabel = new QLabel(tr("0%"), this);
+        m_progressMessageLabel->setAlignment(Qt::AlignRight);
+        m_progressMessageLabel->setVisible(false);
+        downloadProgressTextBlock->addWidget(m_progressMessageLabel);
+        centerBlock->addLayout(downloadProgressTextBlock);
+
+        QHBoxLayout* downloadProgressBlock = new QHBoxLayout();
+        m_progessBar = new QProgressBar(this);
+        m_progessBar->setValue(100);
+        m_progessBar->setVisible(false);
+
+        downloadProgressBlock->addSpacing(10);
+        downloadProgressBlock->addWidget(m_progessBar);
+        downloadProgressBlock->addSpacing(10);
+        centerBlock->addLayout(downloadProgressBlock);
+        contentsLayout->addWidget(templateCenter);
+
+        QSpacerItem* spacer =
+            new QSpacerItem(ProjectTemplateImageWidth, ProjectTemplateImageWidth - 35 - 24, QSizePolicy::Fixed, QSizePolicy::Fixed);
+        contentsLayout->addSpacerItem(spacer);
+
         connect(this, &QAbstractButton::toggled, this, &TemplateButton::onToggled);
     }
 
+    void TemplateButton::SetIsRemote(bool isRemote)
+    {
+        m_cloudIcon->setVisible(isRemote);
+    }
+
+    void TemplateButton::ShowDownloadProgress(bool showProgress)
+    {
+        m_progessBar->setVisible(showProgress);
+        m_progressMessageLabel->setVisible(showProgress);
+        m_darkenOverlay->setVisible(showProgress);
+    }
+
+    void TemplateButton::SetProgressPercentage(float percentage)
+    {
+        m_progessBar->setValue(static_cast<int>(percentage));
+        m_progressMessageLabel->setText(QString("%1%").arg(static_cast<int>(percentage)));
+    }
+
     void TemplateButton::onToggled()
     {
         setProperty("Checked", isChecked());

+ 13 - 0
Code/Tools/ProjectManager/Source/TemplateButtonWidget.h

@@ -12,6 +12,9 @@
 #include <QPushButton>
 #endif
 
+QT_FORWARD_DECLARE_CLASS(QLabel)
+QT_FORWARD_DECLARE_CLASS(QProgressBar)
+
 namespace O3DE::ProjectManager
 {
     class TemplateButton
@@ -23,7 +26,17 @@ namespace O3DE::ProjectManager
         explicit TemplateButton(const QString& imagePath, const QString& labelText, QWidget* parent = nullptr);
         ~TemplateButton() = default;
 
+        void SetIsRemote(bool isRemote);
+        void ShowDownloadProgress(bool showProgress);
+        void SetProgressPercentage(float percent);
+
     protected slots:
         void onToggled();
+
+    private:
+        QLabel* m_cloudIcon = nullptr;
+        QLabel* m_darkenOverlay = nullptr;
+        QLabel* m_progressMessageLabel = nullptr;
+        QProgressBar* m_progessBar = nullptr;
     };
 } // namespace O3DE::ProjectManager

+ 6 - 1
scripts/o3de/o3de/manifest.py

@@ -552,6 +552,7 @@ def get_gem_json_data(gem_name: str = None, gem_path: str or pathlib.Path = None
     if gem_name and not gem_path:
         gem_path = get_registered(gem_name=gem_name, project_path=project_path)
 
+    # Call get_json_data_file if the path is an existing file as get_json_data appends gem.json
     if pathlib.Path(gem_path).is_file():
         return get_json_data_file(gem_path, 'gem', validation.valid_o3de_gem_json)
     else:
@@ -567,7 +568,11 @@ def get_template_json_data(template_name: str = None, template_path: str or path
     if template_name and not template_path:
         template_path = get_registered(template_name=template_name, project_path=project_path)
 
-    return get_json_data('template', template_path, validation.valid_o3de_template_json)
+    # Call get_json_data_file if the path is an existing file as get_json_data appends template.json
+    if pathlib.Path(template_path).is_file():
+        return get_json_data_file(template_path, 'template', validation.valid_o3de_template_json)
+    else:
+        return get_json_data('template', template_path, validation.valid_o3de_template_json)
 
 
 def get_restricted_json_data(restricted_name: str = None, restricted_path: str or pathlib.Path = None,