|
@@ -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)
|