ProjectGemCatalogScreen.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <ProjectGemCatalogScreen.h>
  9. #include <PythonBindingsInterface.h>
  10. #include <GemCatalog/GemCatalogHeaderWidget.h>
  11. #include <GemCatalog/GemDependenciesDialog.h>
  12. #include <GemCatalog/GemFilterWidget.h>
  13. #include <GemCatalog/GemModel.h>
  14. #include <GemCatalog/GemRequirementDialog.h>
  15. #include <ProjectUtils.h>
  16. #include <QDir>
  17. #include <QMessageBox>
  18. #include <QTimer>
  19. namespace O3DE::ProjectManager
  20. {
  21. ProjectGemCatalogScreen::ProjectGemCatalogScreen(DownloadController* downloadController, QWidget* parent)
  22. : GemCatalogScreen(downloadController , /*readOnly = */ false, parent)
  23. {
  24. // We have to fetch the parent of our parent, because Project Manager Gem Catalog is usually embedded inside another workflow, like
  25. // CreateProjectScreen or UpdateProjectScreen
  26. // As such, it does not have direct access to ScreenControls
  27. SetUpScreensControl(parent->parentWidget());
  28. }
  29. ProjectManagerScreen ProjectGemCatalogScreen::GetScreenEnum()
  30. {
  31. return ProjectManagerScreen::ProjectGemCatalog;
  32. }
  33. ProjectGemCatalogScreen::ConfiguredGemsResult ProjectGemCatalogScreen::ConfigureGemsForProject(const QString& projectPath)
  34. {
  35. IPythonBindings* pythonBindings = PythonBindingsInterface::Get();
  36. QVector<QModelIndex> toBeAdded = m_gemModel->GatherGemsToBeAdded();
  37. QVector<QModelIndex> toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
  38. if (m_gemModel->DoGemsToBeAddedHaveRequirements())
  39. {
  40. GemRequirementDialog* confirmRequirementsDialog = new GemRequirementDialog(m_gemModel, this);
  41. if (confirmRequirementsDialog->exec() == QDialog::Rejected)
  42. {
  43. return ConfiguredGemsResult::Cancel;
  44. }
  45. }
  46. if (m_gemModel->HasDependentGemsToRemove())
  47. {
  48. GemDependenciesDialog* dependenciesDialog = new GemDependenciesDialog(m_gemModel, this);
  49. if (dependenciesDialog->exec() == QDialog::Rejected)
  50. {
  51. return ConfiguredGemsResult::Cancel;
  52. }
  53. toBeAdded = m_gemModel->GatherGemsToBeAdded();
  54. toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
  55. }
  56. if (!toBeAdded.isEmpty())
  57. {
  58. QStringList gemPaths, gemNames;
  59. for (const QModelIndex& modelIndex : toBeAdded)
  60. {
  61. // make sure any remote gems we added were downloaded successfully
  62. GemInfo gemInfo = GemModel::GetGemInfo(modelIndex);
  63. const GemInfo::DownloadStatus status = GemModel::GetDownloadStatus(modelIndex);
  64. if (gemInfo.m_gemOrigin == GemInfo::Remote &&
  65. !(status == GemInfo::Downloaded || status == GemInfo::DownloadSuccessful))
  66. {
  67. QMessageBox::critical(
  68. nullptr,
  69. "Cannot add gem that isn't downloaded",
  70. tr("Cannot add gem %1 to project because it isn't downloaded yet or failed to download.")
  71. .arg(GemModel::GetDisplayName(modelIndex)));
  72. return ConfiguredGemsResult::Failed;
  73. }
  74. gemPaths.append(gemInfo.m_path);
  75. // use the version that was selected if available
  76. if (auto gemVersion = GemModel::GetNewVersion(modelIndex); !gemVersion.isEmpty())
  77. {
  78. gemInfo.m_version = gemVersion;
  79. }
  80. gemNames.append(gemInfo.GetNameWithVersionSpecifier());
  81. }
  82. // check compatibility of all gems
  83. auto incompatibleResult = pythonBindings->GetIncompatibleProjectGems(gemPaths, gemNames, projectPath);
  84. if (incompatibleResult && !incompatibleResult.GetValue().empty())
  85. {
  86. const auto& incompatibleGems = incompatibleResult.GetValue();
  87. QString messageBoxQuestion =
  88. gemNames.length() == 1 ? tr("Do you still want to add this gem?") : tr("Do you still want to add these gems?");
  89. QString messageBoxText = QString(tr("%1\n\n%2")).arg(incompatibleGems.join("\n")).arg(messageBoxQuestion);
  90. QMessageBox::StandardButton forceAddGems = QMessageBox::warning(this, tr("Gem compatibility issues found"), messageBoxText,
  91. QMessageBox::Yes | QMessageBox::No);
  92. if (forceAddGems != QMessageBox::StandardButton::Yes)
  93. {
  94. return ConfiguredGemsResult::Cancel;
  95. }
  96. }
  97. // we already checked compatibility, so bypass compatibility checks by using 'force'
  98. constexpr bool force = true;
  99. auto addGemsResult = pythonBindings->AddGemsToProject(gemPaths, gemNames, projectPath, force);
  100. if (!addGemsResult.IsSuccess())
  101. {
  102. QString failureMessage = gemNames.length() == 1 ? tr("Failed to activate gem") : tr("Failed to activate gems");
  103. ProjectUtils::DisplayDetailedError(failureMessage, addGemsResult, this);
  104. AZ_Error("Project Manager", false, failureMessage.toUtf8().constData());
  105. return ConfiguredGemsResult::Failed;
  106. }
  107. else
  108. {
  109. for (const QModelIndex& modelIndex : toBeAdded)
  110. {
  111. GemModel::SetWasPreviouslyAdded(*m_gemModel, modelIndex, true);
  112. // if the user selected a new version then make sure to show that version
  113. const QString& newVersion = GemModel::GetNewVersion(modelIndex);
  114. if (!newVersion.isEmpty())
  115. {
  116. GemModel::UpdateWithVersion(*m_gemModel, modelIndex, newVersion);
  117. GemModel::SetNewVersion(*m_gemModel, modelIndex, "");
  118. }
  119. const auto& gemPath = GemModel::GetGemInfo(modelIndex).m_path;
  120. // register external gems that were added with relative paths
  121. if (m_gemsToRegisterWithProject.contains(gemPath))
  122. {
  123. pythonBindings->RegisterGem(QDir(projectPath).relativeFilePath(gemPath), projectPath);
  124. }
  125. }
  126. }
  127. }
  128. for (const QModelIndex& modelIndex : toBeRemoved)
  129. {
  130. const auto result = pythonBindings->RemoveGemFromProject(GemModel::GetName(modelIndex), projectPath);
  131. if (!result.IsSuccess())
  132. {
  133. QMessageBox::critical(
  134. nullptr, "Failed to remove gem from project",
  135. tr("Cannot remove gem %1 from project.<br><br>Error:<br>%2")
  136. .arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
  137. return ConfiguredGemsResult::Failed;
  138. }
  139. else
  140. {
  141. GemModel::SetWasPreviouslyAdded(*m_gemModel, modelIndex, false);
  142. }
  143. }
  144. return ConfiguredGemsResult::Success;
  145. }
  146. bool ProjectGemCatalogScreen::IsTab()
  147. {
  148. return false;
  149. }
  150. } // namespace O3DE::ProjectManager