CreateProjectCtrl.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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 <CreateProjectCtrl.h>
  9. #include <ScreensCtrl.h>
  10. #include <PythonBindingsInterface.h>
  11. #include <NewProjectSettingsScreen.h>
  12. #include <ScreenHeaderWidget.h>
  13. #include <GemCatalog/GemModel.h>
  14. #include <ProjectGemCatalogScreen.h>
  15. #include <GemRepo/GemRepoScreen.h>
  16. #include <ProjectUtils.h>
  17. #include <DownloadController.h>
  18. #include <QDialogButtonBox>
  19. #include <QHBoxLayout>
  20. #include <QVBoxLayout>
  21. #include <QPushButton>
  22. #include <QMessageBox>
  23. #include <QStackedWidget>
  24. #include <QLabel>
  25. #include <QSizePolicy>
  26. namespace O3DE::ProjectManager
  27. {
  28. CreateProjectCtrl::CreateProjectCtrl(DownloadController* downloadController, QWidget* parent)
  29. : ScreenWidget(parent)
  30. {
  31. QVBoxLayout* vLayout = new QVBoxLayout();
  32. vLayout->setContentsMargins(0,0,0,0);
  33. m_header = new ScreenHeader(this);
  34. m_header->setTitle(tr("Create a New Project"));
  35. m_header->setSubTitle(tr("Enter Project Details"));
  36. connect(m_header->backButton(), &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton);
  37. vLayout->addWidget(m_header);
  38. m_stack = new QStackedWidget(this);
  39. m_stack->setObjectName("body");
  40. m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Expanding));
  41. m_newProjectSettingsScreen = new NewProjectSettingsScreen(downloadController, this);
  42. m_stack->addWidget(m_newProjectSettingsScreen);
  43. m_projectGemCatalogScreen = new ProjectGemCatalogScreen(downloadController, this);
  44. m_stack->addWidget(m_projectGemCatalogScreen);
  45. m_gemRepoScreen = new GemRepoScreen(this);
  46. m_stack->addWidget(m_gemRepoScreen);
  47. vLayout->addWidget(m_stack);
  48. connect(m_projectGemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest);
  49. connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, m_projectGemCatalogScreen, &ProjectGemCatalogScreen::Refresh);
  50. // When there are multiple project templates present, we re-gather the gems when changing the selected the project template.
  51. connect(m_newProjectSettingsScreen, &NewProjectSettingsScreen::OnTemplateSelectionChanged, this, [=](int oldIndex, [[maybe_unused]] int newIndex)
  52. {
  53. const GemModel* gemModel = m_projectGemCatalogScreen->GetGemModel();
  54. const QVector<QModelIndex> toBeAdded = gemModel->GatherGemsToBeAdded();
  55. const QVector<QModelIndex> toBeRemoved = gemModel->GatherGemsToBeRemoved();
  56. if (!toBeAdded.isEmpty() || !toBeRemoved.isEmpty())
  57. {
  58. // In case the user enabled or disabled any gem and the current selection does not match the default from the
  59. // // project template anymore, we need to ask the user if they want to proceed as their modifications will be lost.
  60. const QString title = tr("Modifications will be lost");
  61. const QString text = tr("You selected a new project template after modifying the enabled gems.\n\n"
  62. "All modifications will be lost and the default from the new project template will be used.\n\n"
  63. "Do you want to proceed?");
  64. if (QMessageBox::warning(this, title, text, QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
  65. {
  66. // The users wants to proceed. Reinitialize based on the newly selected project template.
  67. ReinitGemCatalogForSelectedTemplate();
  68. }
  69. else
  70. {
  71. // Roll-back to the previously selected project template and
  72. // block signals so that we don't end up in this same callback again.
  73. m_newProjectSettingsScreen->SelectProjectTemplate(oldIndex, /*blockSignals=*/true);
  74. }
  75. }
  76. else
  77. {
  78. // In case the user did not enable or disable any gem and the currently enabled gems matches the previously selected
  79. // ones from the project template, we can just reinitialize based on the newly selected project template.
  80. ReinitGemCatalogForSelectedTemplate();
  81. }
  82. });
  83. QDialogButtonBox* buttons = new QDialogButtonBox();
  84. buttons->setObjectName("footer");
  85. vLayout->addWidget(buttons);
  86. m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole);
  87. connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton);
  88. connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest);
  89. m_secondaryButton = buttons->addButton(tr("Back"), QDialogButtonBox::RejectRole);
  90. m_secondaryButton->setProperty("secondary", true);
  91. m_secondaryButton->setVisible(false);
  92. connect(m_secondaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleSecondaryButton);
  93. Update();
  94. setLayout(vLayout);
  95. }
  96. ProjectManagerScreen CreateProjectCtrl::GetScreenEnum()
  97. {
  98. return ProjectManagerScreen::CreateProject;
  99. }
  100. // Called when pressing "Create New Project"
  101. void CreateProjectCtrl::NotifyCurrentScreen()
  102. {
  103. ScreenWidget* currentScreen = reinterpret_cast<ScreenWidget*>(m_stack->currentWidget());
  104. if (currentScreen)
  105. {
  106. currentScreen->NotifyCurrentScreen();
  107. }
  108. // Gather the enabled gems from the default project template when starting the create new project workflow.
  109. ReinitGemCatalogForSelectedTemplate();
  110. // make sure the gem repo has the latest details
  111. m_gemRepoScreen->Reinit();
  112. }
  113. void CreateProjectCtrl::HandleBackButton()
  114. {
  115. if (m_stack->currentIndex() > 0)
  116. {
  117. PreviousScreen();
  118. }
  119. else
  120. {
  121. emit GoToPreviousScreenRequest();
  122. }
  123. }
  124. void CreateProjectCtrl::HandleSecondaryButton()
  125. {
  126. if (m_stack->currentIndex() > 0)
  127. {
  128. // return to Project Settings page
  129. PreviousScreen();
  130. }
  131. else
  132. {
  133. // Configure Gems
  134. NextScreen();
  135. }
  136. }
  137. void CreateProjectCtrl::Update()
  138. {
  139. if (m_stack->currentWidget() == m_projectGemCatalogScreen)
  140. {
  141. m_header->setSubTitle(tr("Configure project with Gems"));
  142. m_secondaryButton->setVisible(false);
  143. m_primaryButton->setVisible(true);
  144. }
  145. else if (m_stack->currentWidget() == m_gemRepoScreen)
  146. {
  147. m_header->setSubTitle(tr("Remote Sources"));
  148. m_secondaryButton->setVisible(true);
  149. m_secondaryButton->setText(tr("Back"));
  150. m_primaryButton->setVisible(false);
  151. }
  152. else
  153. {
  154. m_header->setSubTitle(tr("Enter Project Details"));
  155. m_secondaryButton->setVisible(true);
  156. m_secondaryButton->setText(tr("Configure Gems"));
  157. m_primaryButton->setVisible(true);
  158. }
  159. }
  160. void CreateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen)
  161. {
  162. if (screen == ProjectManagerScreen::ProjectGemCatalog)
  163. {
  164. HandleSecondaryButton();
  165. }
  166. else if (screen == ProjectManagerScreen::GemRepos)
  167. {
  168. NextScreen();
  169. }
  170. else
  171. {
  172. emit ChangeScreenRequest(screen);
  173. }
  174. }
  175. void CreateProjectCtrl::NextScreen()
  176. {
  177. if (m_stack->currentIndex() < m_stack->count())
  178. {
  179. if(CurrentScreenIsValid())
  180. {
  181. m_stack->setCurrentIndex(m_stack->currentIndex() + 1);
  182. Update();
  183. }
  184. else
  185. {
  186. QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again."));
  187. }
  188. }
  189. }
  190. void CreateProjectCtrl::PreviousScreen()
  191. {
  192. // we don't require the current screen to be valid when moving back
  193. if (m_stack->currentIndex() > 0)
  194. {
  195. m_stack->setCurrentIndex(m_stack->currentIndex() - 1);
  196. Update();
  197. }
  198. }
  199. void CreateProjectCtrl::HandlePrimaryButton()
  200. {
  201. CreateProject();
  202. }
  203. bool CreateProjectCtrl::CurrentScreenIsValid()
  204. {
  205. if (m_stack->currentWidget() == m_newProjectSettingsScreen)
  206. {
  207. return m_newProjectSettingsScreen->Validate().IsSuccess();
  208. }
  209. return true;
  210. }
  211. void CreateProjectCtrl::CreateProject()
  212. {
  213. AZ::Outcome<void, QString> settingsValidation = m_newProjectSettingsScreen->Validate();
  214. if (settingsValidation.IsSuccess())
  215. {
  216. if (!m_projectGemCatalogScreen->GetDownloadController()->IsDownloadQueueEmpty())
  217. {
  218. QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing."));
  219. return;
  220. }
  221. ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo();
  222. QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
  223. auto result = PythonBindingsInterface::Get()->CreateProject(projectTemplatePath, projectInfo);
  224. if (result.IsSuccess())
  225. {
  226. // don't need to register here, the project is already registered in CreateProject()
  227. const ProjectGemCatalogScreen::ConfiguredGemsResult gemResult = m_projectGemCatalogScreen->ConfigureGemsForProject(projectInfo.m_path);
  228. if (gemResult == ProjectGemCatalogScreen::ConfiguredGemsResult::Failed)
  229. {
  230. QMessageBox::critical(this, tr("Failed to configure gems"), tr("Failed to configure gems for template."));
  231. }
  232. if (gemResult != ProjectGemCatalogScreen::ConfiguredGemsResult::Success)
  233. {
  234. return;
  235. }
  236. projectInfo.m_needsBuild = true;
  237. emit NotifyBuildProject(projectInfo);
  238. emit ChangeScreenRequest(ProjectManagerScreen::Projects);
  239. }
  240. else
  241. {
  242. QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project."));
  243. }
  244. }
  245. else
  246. {
  247. const QString& errorMessage = settingsValidation.GetError();
  248. if (errorMessage.isEmpty())
  249. {
  250. QMessageBox::warning(
  251. this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again."));
  252. }
  253. else
  254. {
  255. QMessageBox::warning(this, tr("Invalid project settings"), errorMessage);
  256. }
  257. }
  258. }
  259. void CreateProjectCtrl::ReinitGemCatalogForSelectedTemplate()
  260. {
  261. const QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
  262. m_projectGemCatalogScreen->ReinitForProject(projectTemplatePath + "/Template");
  263. }
  264. } // namespace O3DE::ProjectManager