Преглед на файлове

only update the Projects screen when it is visible

The Project screen was getting reset 3+ times on startup and 3+ times after actions on other screens and calling

Signed-off-by: Alex Peterson <[email protected]>
Alex Peterson преди 2 години
родител
ревизия
57aa730312

+ 149 - 139
Code/Tools/ProjectManager/Source/ProjectsScreen.cpp

@@ -173,8 +173,6 @@ namespace O3DE::ProjectManager
             projectsScrollArea->setWidget(scrollWidget);
             projectsScrollArea->setWidgetResizable(true);
 
-            ResetProjectsContent();
-
             layout->addWidget(projectsScrollArea);
         }
 
@@ -207,109 +205,51 @@ namespace O3DE::ProjectManager
         return projectButton;
     }
 
-    void ProjectsScreen::ResetProjectsContent()
+    void ProjectsScreen::RemoveProjectButtonsFromFlowLayout(const QVector<ProjectInfo>& projectsToKeep)
     {
-        RemoveInvalidProjects();
-
-        // Get all projects and sort so that building and queued projects appear first
-        // followed by the remaining projects in alphabetical order
-        QVector<ProjectInfo> projects;
-        auto projectsResult = PythonBindingsInterface::Get()->GetProjects();
-        if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty())
+        // If a project path is in this set then the button for it will be kept
+        AZStd::unordered_set<AZ::IO::Path> keepProject;
+        for (const ProjectInfo& project : projectsToKeep)
         {
-            projects.append(projectsResult.GetValue());
+            keepProject.insert(project.m_path.toUtf8().constData());
         }
 
-        // Also add remote projects that we do not have a local copy of
-        auto remoteProjectsResult = PythonBindingsInterface::Get()->GetProjectsForAllRepos();
-        if (remoteProjectsResult.IsSuccess() && !remoteProjectsResult.GetValue().isEmpty())
+        // Remove buttons from flow layout and delete buttons for removed projects 
+        auto projectButtonsIter = m_projectButtons.begin();
+        while (projectButtonsIter != m_projectButtons.end())
         {
-            const QVector<ProjectInfo>& remoteProjects{ remoteProjectsResult.TakeValue() };
-            for (const ProjectInfo& remoteProject : remoteProjects)
-            {
-                auto foundProject = AZStd::ranges::find_if(
-                    projects,
-                    [&remoteProject](const ProjectInfo& value)
-                    {
-                        return remoteProject.m_id == value.m_id;
-                    });
-                if (foundProject == projects.end())
-                {
-                    projects.append(remoteProject);
-                }
-            }
-        }
+            const auto button = projectButtonsIter->second;
+            m_projectsFlowLayout->removeWidget(button);
 
-        if (!projects.isEmpty())
-        {
-            // If a project path is in this set then the button for it will be kept
-            AZStd::unordered_set<AZ::IO::Path> keepProject;
-            for (const ProjectInfo& project : projects)
+            if (!keepProject.contains(projectButtonsIter->first))
             {
-                keepProject.insert(project.m_path.toUtf8().constData());
+                m_fileSystemWatcher->removePath(QDir::toNativeSeparators(button->GetProjectInfo().m_path + "/project.json"));
+                button->deleteLater();
+                projectButtonsIter = m_projectButtons.erase(projectButtonsIter);
             }
-
-            // Remove buttons from flow layout and delete buttons for removed projects 
-            auto projectButtonsIter = m_projectButtons.begin();
-            while (projectButtonsIter != m_projectButtons.end())
-            {
-                const auto button = projectButtonsIter->second;
-                m_projectsFlowLayout->removeWidget(button);
-
-                if (!keepProject.contains(projectButtonsIter->first))
-                {
-                    m_fileSystemWatcher->removePath(QDir::toNativeSeparators(button->GetProjectInfo().m_path + "/project.json"));
-                    button->deleteLater();
-                    projectButtonsIter = m_projectButtons.erase(projectButtonsIter);
-                }
-                else
-                {
-                    ++projectButtonsIter;
-                }
-            }
-
-            AZ::IO::Path buildProjectPath;
-            if (m_currentBuilder)
+            else
             {
-                buildProjectPath = AZ::IO::Path(m_currentBuilder->GetProjectInfo().m_path.toUtf8().constData());
+                ++projectButtonsIter;
             }
+        }
+    }
 
-            // Put currently building project in front, then queued projects, then sorts alphabetically
-            AZStd::sort(projects.begin(), projects.end(), [buildProjectPath, this](const ProjectInfo& arg1, const ProjectInfo& arg2)
-            {
-                if (!buildProjectPath.empty())
-                {
-                    if (AZ::IO::Path(arg1.m_path.toUtf8().constData()) == buildProjectPath)
-                    {
-                        return true;
-                    }
-                    else if (AZ::IO::Path(arg2.m_path.toUtf8().constData()) == buildProjectPath)
-                    {
-                        return false;
-                    }
-                }
+    void ProjectsScreen::UpdateIfCurrentScreen()
+    {
+        if (IsCurrentScreen())
+        {
+            UpdateWithProjects(GetAllProjects());
+        }
+    }
 
-                bool arg1InBuildQueue = BuildQueueContainsProject(arg1.m_path);
-                bool arg2InBuildQueue = BuildQueueContainsProject(arg2.m_path);
-                if (arg1InBuildQueue && !arg2InBuildQueue)
-                {
-                    return true;
-                }
-                else if (!arg1InBuildQueue && arg2InBuildQueue)
-                {
-                    return false;
-                }
-                else if (arg1.m_displayName.compare(arg2.m_displayName, Qt::CaseInsensitive) == 0)
-                {
-                    // handle case where names are the same
-                    return arg1.m_path.toLower() < arg2.m_path.toLower();
-                }
-                else
-                {
-                    return arg1.m_displayName.toLower() < arg2.m_displayName.toLower();
-                }
-            });
+    void ProjectsScreen::UpdateWithProjects(const QVector<ProjectInfo>& projects)
+    {
+        PythonBindingsInterface::Get()->RemoveInvalidProjects();
 
+        if (!projects.isEmpty())
+        {
+            // Remove all existing buttons before adding them back in the correct order
+            RemoveProjectButtonsFromFlowLayout(/*projectsToKeep*/ projects);
 
             // It's more efficient to update the project engine by loading engine infos once
             // instead of loading them all each time we want to know what project an engine uses
@@ -352,42 +292,45 @@ namespace O3DE::ProjectManager
                 }
 
                 // Check whether project manager has successfully built the project
-                if (currentButton)
-                {
-                    m_projectsFlowLayout->addWidget(currentButton);
+                AZ_Assert(currentButton, "Invalid ProjectButton");
 
-                    bool projectBuiltSuccessfully = false;
-                    SettingsInterface::Get()->GetProjectBuiltSuccessfully(projectBuiltSuccessfully, project);
+                m_projectsFlowLayout->addWidget(currentButton);
 
-                    if (!projectBuiltSuccessfully)
-                    {
-                        currentButton->SetState(ProjectButtonState::NeedsToBuild);
-                    }
+                bool projectBuiltSuccessfully = false;
+                SettingsInterface::Get()->GetProjectBuiltSuccessfully(projectBuiltSuccessfully, project);
+                if (!projectBuiltSuccessfully)
+                {
+                    currentButton->SetState(ProjectButtonState::NeedsToBuild);
+                }
 
-                    if (project.m_remote)
-                    {
-                        currentButton->SetState(ProjectButtonState::NotDownloaded);
-                        currentButton->SetProjectButtonAction(
-                            tr("Download Project"),
-                            [this, currentButton, project]
-                            {
-                                m_downloadController->AddObjectDownload(project.m_projectName, "", DownloadController::DownloadObjectType::Project);
-                                currentButton->SetState(ProjectButtonState::Downloading);
-                            });
-                    }
+                if (project.m_remote)
+                {
+                    currentButton->SetState(ProjectButtonState::NotDownloaded);
+                    currentButton->SetProjectButtonAction(
+                        tr("Download Project"),
+                        [this, currentButton, project]
+                        {
+                            m_downloadController->AddObjectDownload(project.m_projectName, "", DownloadController::DownloadObjectType::Project);
+                            currentButton->SetState(ProjectButtonState::Downloading);
+                        });
                 }
             }
 
-            // Setup building button again
-            if (!buildProjectPath.empty())
+            if (m_currentBuilder)
             {
-                auto buildProjectIter = m_projectButtons.find(buildProjectPath);
-                if (buildProjectIter != m_projectButtons.end())
+                AZ::IO::Path buildProjectPath = AZ::IO::Path(m_currentBuilder->GetProjectInfo().m_path.toUtf8().constData());
+                if (!buildProjectPath.empty())
                 {
-                    m_currentBuilder->SetProjectButton(buildProjectIter->second);
+                    // Setup building button again
+                    auto buildProjectIter = m_projectButtons.find(buildProjectPath);
+                    if (buildProjectIter != m_projectButtons.end())
+                    {
+                        m_currentBuilder->SetProjectButton(buildProjectIter->second);
+                    }
                 }
             }
 
+            // Let the user can cancel builds for projects in the build queue
             for (const ProjectInfo& project : m_buildQueue)
             {
                 auto projectIter = m_projectButtons.find(project.m_path.toUtf8().constData());
@@ -403,6 +346,7 @@ namespace O3DE::ProjectManager
                 }
             }
 
+            // Update the project build status if it requires building
             for (const ProjectInfo& project : m_requiresBuild)
             {
                 auto projectIter = m_projectButtons.find(project.m_path.toUtf8().constData());
@@ -446,7 +390,7 @@ namespace O3DE::ProjectManager
     void ProjectsScreen::HandleProjectFilePathChanged(const QString& /*path*/)
     {
         // QFileWatcher automatically stops watching the path if it was removed so we will just refresh our view
-        ResetProjectsContent();
+        UpdateIfCurrentScreen();
     }
 
     ProjectManagerScreen ProjectsScreen::GetScreenEnum()
@@ -685,7 +629,8 @@ namespace O3DE::ProjectManager
         {
             m_requiresBuild.append(projectInfo);
         }
-        ResetProjectsContent();
+
+        UpdateIfCurrentScreen();
 
         if (showMessage)
         {
@@ -718,7 +663,7 @@ namespace O3DE::ProjectManager
             else
             {
                 m_buildQueue.append(projectInfo);
-                ResetProjectsContent();
+                UpdateIfCurrentScreen();
             }
         }
     }
@@ -726,7 +671,7 @@ namespace O3DE::ProjectManager
     void ProjectsScreen::UnqueueBuildProject(const ProjectInfo& projectInfo)
     {
         m_buildQueue.removeAll(projectInfo);
-        ResetProjectsContent();
+        UpdateIfCurrentScreen();
     }
 
     void ProjectsScreen::StartProjectDownload(const QString& projectName, const QString& destinationPath, bool queueBuild)
@@ -787,7 +732,7 @@ namespace O3DE::ProjectManager
         }
         else
         {
-            ResetProjectsContent();
+            UpdateIfCurrentScreen();
         }
     }
 
@@ -812,9 +757,83 @@ namespace O3DE::ProjectManager
         }
     }
 
+    QVector<ProjectInfo> ProjectsScreen::GetAllProjects()
+    {
+        QVector<ProjectInfo> projects;
+
+        auto projectsResult = PythonBindingsInterface::Get()->GetProjects();
+        if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty())
+        {
+            projects.append(projectsResult.GetValue());
+        }
+
+        auto remoteProjectsResult = PythonBindingsInterface::Get()->GetProjectsForAllRepos();
+        if (remoteProjectsResult.IsSuccess() && !remoteProjectsResult.GetValue().isEmpty())
+        {
+            for (const ProjectInfo& remoteProject : remoteProjectsResult.TakeValue())
+            {
+                auto foundProject = AZStd::ranges::find_if( projects,
+                    [&remoteProject](const ProjectInfo& value)
+                    {
+                        return remoteProject.m_id == value.m_id;
+                    });
+                if (foundProject == projects.end())
+                {
+                    projects.append(remoteProject);
+                }
+            }
+        }
+
+        AZ::IO::Path buildProjectPath;
+        if (m_currentBuilder)
+        {
+            buildProjectPath = AZ::IO::Path(m_currentBuilder->GetProjectInfo().m_path.toUtf8().constData());
+        }
+
+        // Sort the projects, putting currently building project in front, then queued projects, then sorts alphabetically
+        AZStd::sort(projects.begin(), projects.end(), [buildProjectPath, this](const ProjectInfo& arg1, const ProjectInfo& arg2)
+        {
+            if (!buildProjectPath.empty())
+            {
+                if (AZ::IO::Path(arg1.m_path.toUtf8().constData()) == buildProjectPath)
+                {
+                    return true;
+                }
+                else if (AZ::IO::Path(arg2.m_path.toUtf8().constData()) == buildProjectPath)
+                {
+                    return false;
+                }
+            }
+
+            bool arg1InBuildQueue = BuildQueueContainsProject(arg1.m_path);
+            bool arg2InBuildQueue = BuildQueueContainsProject(arg2.m_path);
+            if (arg1InBuildQueue && !arg2InBuildQueue)
+            {
+                return true;
+            }
+            else if (!arg1InBuildQueue && arg2InBuildQueue)
+            {
+                return false;
+            }
+            else if (arg1.m_displayName.compare(arg2.m_displayName, Qt::CaseInsensitive) == 0)
+            {
+                // handle case where names are the same
+                return arg1.m_path.toLower() < arg2.m_path.toLower();
+            }
+            else
+            {
+                return arg1.m_displayName.toLower() < arg2.m_displayName.toLower();
+            }
+        });
+
+        return projects;
+    }
+
     void ProjectsScreen::NotifyCurrentScreen()
     {
-        if (ShouldDisplayFirstTimeContent())
+        const QVector<ProjectInfo>& projects = GetAllProjects();
+        const bool projectsFound = !projects.isEmpty();
+        if (ShouldDisplayFirstTimeContent(projectsFound))
         {
             m_background.load(":/Backgrounds/FtueBackground.jpg");
             m_stack->setCurrentWidget(m_firstTimeContent);
@@ -822,22 +841,18 @@ namespace O3DE::ProjectManager
         else
         {
             m_background.load(":/Backgrounds/DefaultBackground.jpg");
-            ResetProjectsContent();
+            UpdateWithProjects(projects);
         }
     }
 
-    bool ProjectsScreen::ShouldDisplayFirstTimeContent()
+    bool ProjectsScreen::ShouldDisplayFirstTimeContent(bool projectsFound)
     {
-        auto projectsResult = PythonBindingsInterface::Get()->GetProjects();
-        auto remoteProjectsResult = PythonBindingsInterface::Get()->GetProjectsForAllRepos();
-
-        // If we do not have any local or remote projects to show, then show the first time content
-        if ((!projectsResult.IsSuccess() || projectsResult.GetValue().isEmpty()) &&
-            (!remoteProjectsResult.IsSuccess() || remoteProjectsResult.GetValue().isEmpty()))
+        if (projectsFound)
         {
-            return true;
+            return false;
         }
 
+        // only show this screen once
         QSettings settings;
         bool displayFirstTimeContent = settings.value("displayFirstTimeContent", true).toBool();
         if (displayFirstTimeContent)
@@ -848,11 +863,6 @@ namespace O3DE::ProjectManager
         return displayFirstTimeContent;
     }
 
-    bool ProjectsScreen::RemoveInvalidProjects()
-    {
-        return PythonBindingsInterface::Get()->RemoveInvalidProjects();
-    }
-
     bool ProjectsScreen::StartProjectBuild(const ProjectInfo& projectInfo)
     {
         if (ProjectUtils::FindSupportedCompiler(this))
@@ -866,7 +876,7 @@ namespace O3DE::ProjectManager
             if (buildProject == QMessageBox::Yes)
             {
                 m_currentBuilder = new ProjectBuilderController(projectInfo, nullptr, this);
-                ResetProjectsContent();
+                UpdateWithProjects(GetAllProjects());
                 connect(m_currentBuilder, &ProjectBuilderController::Done, this, &ProjectsScreen::ProjectBuildDone);
                 connect(m_currentBuilder, &ProjectBuilderController::NotifyBuildProject, this, &ProjectsScreen::SuggestBuildProject);
 
@@ -909,7 +919,7 @@ namespace O3DE::ProjectManager
             m_buildQueue.pop_front();
         }
 
-        ResetProjectsContent();
+        UpdateIfCurrentScreen();
     }
 
     QList<ProjectInfo>::iterator ProjectsScreen::RequiresBuildProjectIterator(const QString& projectPath)

+ 6 - 3
Code/Tools/ProjectManager/Source/ProjectsScreen.h

@@ -17,6 +17,7 @@
 #include <DownloadController.h>
 
 #include <QQueue>
+#include <QVector>
 #endif
 
 QT_FORWARD_DECLARE_CLASS(QPaintEvent)
@@ -77,9 +78,11 @@ namespace O3DE::ProjectManager
         QFrame* CreateFirstTimeContent();
         QFrame* CreateProjectsContent();
         ProjectButton* CreateProjectButton(const ProjectInfo& project, const EngineInfo& engine);
-        void ResetProjectsContent();
-        bool ShouldDisplayFirstTimeContent();
-        bool RemoveInvalidProjects();
+        QVector<ProjectInfo> GetAllProjects();
+        void UpdateWithProjects(const QVector<ProjectInfo>& projects);
+        void UpdateIfCurrentScreen();
+        bool ShouldDisplayFirstTimeContent(bool projectsFound);
+        void RemoveProjectButtonsFromFlowLayout(const QVector<ProjectInfo>& projectsToKeep);
 
         bool StartProjectBuild(const ProjectInfo& projectInfo);
         QList<ProjectInfo>::iterator RequiresBuildProjectIterator(const QString& projectPath);

+ 14 - 0
Code/Tools/ProjectManager/Source/ScreenWidget.h

@@ -10,6 +10,7 @@
 #if !defined(Q_MOC_RUN)
 #include <ScreenDefs.h>
 #include <ProjectInfo.h>
+#include <ScreensCtrl.h>
 
 #include <QWidget>
 #include <QStyleOption>
@@ -34,14 +35,17 @@ namespace O3DE::ProjectManager
         {
             return ProjectManagerScreen::Empty;
         }
+
         virtual bool IsReadyForNextScreen()
         {
             return true;
         }
+
         virtual bool IsTab()
         {
             return false;
         }
+
         virtual QString GetTabText()
         {
             return tr("Missing");
@@ -51,12 +55,22 @@ namespace O3DE::ProjectManager
         {
             return GetScreenEnum() == screen;
         }
+
         virtual void GoToScreen([[maybe_unused]] ProjectManagerScreen screen)
         {
         }
+
         virtual void Init()
         {
         }
+
+        //! Returns true if this screen is the current screen 
+        virtual bool IsCurrentScreen()
+        {
+            ScreensCtrl* screensCtrl = qobject_cast<ScreensCtrl*>(parent());
+            return screensCtrl ? screensCtrl->GetCurrentScreen() == this : false;
+        }
+
         //! Notify this screen it is the current screen 
         virtual void NotifyCurrentScreen()
         {