2
0

SeedTabWidget.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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 <source/ui/SeedTabWidget.h>
  9. #include <source/ui/ui_SeedTabWidget.h>
  10. #include <source/utils/GUIApplicationManager.h>
  11. #include <source/ui/AddSeedDialog.h>
  12. #include <source/ui/EditSeedDialog.h>
  13. #include <source/ui/NewFileDialog.h>
  14. #include <source/models/SeedListFileTableModel.h>
  15. #include <source/models/SeedListTableModel.h>
  16. #include <AzCore/Outcome/Outcome.h>
  17. #include <AzCore/Utils/Utils.h>
  18. #include <AzFramework/StringFunc/StringFunc.h>
  19. #include <AzQtComponents/Components/FilteredSearchWidget.h>
  20. #include <AzToolsFramework/Asset/AssetSeedManager.h>
  21. #include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalog.h>
  22. #include <QCheckBox>
  23. #include <QFileDialog>
  24. #include <QItemSelection>
  25. #include <QItemSelectionRange>
  26. #include <QMessageBox>
  27. #include <QPushButton>
  28. const char GenerateAssetListFilesDialogName[] = "Generate Asset List Files";
  29. const int CheckBoxTableIndentationSize = 2; // when the indentation is 0, the checkboxes are too close to the edge
  30. namespace AssetBundler
  31. {
  32. SeedTabWidget::SeedTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager, const QString& assetBundlingDirectory)
  33. : AssetBundlerTabWidget(parent, guiApplicationManager)
  34. , m_fileTableModel(new SeedListFileTableModel(this))
  35. , m_seedListContentsModel(new SeedListTableModel())
  36. {
  37. AZ_UNUSED(assetBundlingDirectory);
  38. m_ui.reset(new Ui::SeedTabWidget);
  39. m_ui->setupUi(this);
  40. m_ui->mainVerticalLayout->setContentsMargins(MarginSize, MarginSize, MarginSize, MarginSize);
  41. AZ::Debug::TraceMessageBus::Handler::BusConnect();
  42. // File view of all Seed List Files
  43. m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel(
  44. this,
  45. m_fileTableModel->GetFileNameColumnIndex(),
  46. m_fileTableModel->GetTimeStampColumnIndex()));
  47. m_fileTableFilterModel->setSourceModel(m_fileTableModel.data());
  48. m_ui->fileTableView->setModel(m_fileTableFilterModel.data());
  49. connect(m_ui->fileFilteredSearchWidget,
  50. &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
  51. m_fileTableFilterModel.data(),
  52. static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetBundlerFileTableFilterModel::FilterChanged));
  53. connect(m_ui->fileTableView->selectionModel(),
  54. &QItemSelectionModel::selectionChanged,
  55. this,
  56. &SeedTabWidget::FileSelectionChanged);
  57. m_ui->fileTableView->setIndentation(CheckBoxTableIndentationSize);
  58. // New File Button
  59. connect(m_ui->createNewSeedListButton, &QPushButton::clicked, this, &SeedTabWidget::OnNewFileButtonPressed);
  60. // Select Default Seed Lists
  61. connect(m_ui->selectDefaultSeedListsCheckBox, &QCheckBox::clicked, this, &SeedTabWidget::OnSelectDefaultSeedListsCheckBoxChanged);
  62. // Generate Asset Lists Button
  63. SetGenerateAssetListsButtonEnabled(false);
  64. connect(m_ui->generateAssetListsButton, &QPushButton::clicked, this, &SeedTabWidget::OnGenerateAssetListsButtonPressed);
  65. // Table that displays the contents of a Seed List File
  66. m_seedListContentsFilterModel.reset(new AssetBundlerFileTableFilterModel(this, SeedListTableModel::Column::ColumnRelativePath));
  67. m_seedListContentsFilterModel->setSourceModel(m_seedListContentsModel.data());
  68. m_ui->seedFileContentsTable->setModel(m_seedListContentsFilterModel.data());
  69. connect(m_ui->seedListContentsFilteredSearchWidget,
  70. &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
  71. m_seedListContentsFilterModel.data(),
  72. static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetBundlerFileTableFilterModel::FilterChanged));
  73. m_ui->seedFileContentsTable->setContextMenuPolicy(Qt::CustomContextMenu);
  74. connect(m_ui->seedFileContentsTable,
  75. &QTreeView::customContextMenuRequested,
  76. this,
  77. &SeedTabWidget::OnSeedListContentsTableContextMenuRequested);
  78. m_ui->seedFileContentsTable->setIndentation(0);
  79. // Edit All Platforms
  80. connect(m_ui->editAllSeedsButton, &QPushButton::clicked, this, &SeedTabWidget::OnEditAllButtonPressed);
  81. // Add Seed
  82. connect(m_ui->addSeedButton, &QPushButton::clicked, this, &SeedTabWidget::OnAddSeedButtonPressed);
  83. SetModelDataSource();
  84. }
  85. SeedTabWidget::~SeedTabWidget()
  86. {
  87. AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
  88. }
  89. bool SeedTabWidget::HasUnsavedChanges()
  90. {
  91. return m_fileTableModel->HasUnsavedChanges();
  92. }
  93. void SeedTabWidget::Reload()
  94. {
  95. // Reload all the seed list files
  96. m_fileTableModel->Reload(
  97. AzToolsFramework::AssetSeedManager::GetSeedFileExtension(),
  98. m_watchedFolders,
  99. m_watchedFiles,
  100. m_filePathToGemNameMap);
  101. // Update the selected row
  102. FileSelectionChanged();
  103. }
  104. bool SeedTabWidget::SaveCurrentSelection()
  105. {
  106. return m_fileTableModel->Save(m_selectedFileTableIndex);
  107. }
  108. bool SeedTabWidget::SaveAll()
  109. {
  110. return m_fileTableModel->SaveAll();
  111. }
  112. void SeedTabWidget::SetModelDataSource()
  113. {
  114. // Remove the current watched folders and files
  115. m_guiApplicationManager->RemoveWatchedPaths(m_watchedFolders + m_watchedFiles);
  116. // Set the new watched folders for the model
  117. m_watchedFolders.clear();
  118. m_watchedFolders.insert(m_guiApplicationManager->GetSeedListsFolder().c_str());
  119. // Get the list of default Seed List files
  120. m_filePathToGemNameMap = AssetBundler::GetDefaultSeedListFiles(
  121. AZStd::string_view{ AZ::Utils::GetEnginePath() },
  122. m_guiApplicationManager->GetCurrentProjectName(),
  123. m_guiApplicationManager->GetGemInfoList(), m_guiApplicationManager->GetEnabledPlatforms());
  124. // Get the list of default Seeds that are not stored in a Seed List file on-disk
  125. AZStd::vector<AZStd::string> defaultSeeds =
  126. GetDefaultSeeds(AZ::Utils::GetProjectPath(), m_guiApplicationManager->GetCurrentProjectName());
  127. m_fileTableModel->AddDefaultSeedsToInMemoryList(
  128. defaultSeeds,
  129. m_guiApplicationManager->GetCurrentProjectName().c_str(),
  130. m_guiApplicationManager->GetEnabledPlatforms());
  131. // Set the new watched filess for the model
  132. m_watchedFiles.clear();
  133. for (const auto& it : m_filePathToGemNameMap)
  134. {
  135. m_watchedFiles.insert(it.first.c_str());
  136. }
  137. ReadScanPathsFromAssetBundlerSettings(AssetBundlingFileType::SeedListFileType);
  138. m_guiApplicationManager->AddWatchedPaths(m_watchedFolders + m_watchedFiles);
  139. }
  140. AzQtComponents::TableView* SeedTabWidget::GetFileTableView()
  141. {
  142. return m_ui->fileTableView;
  143. }
  144. QModelIndex SeedTabWidget::GetSelectedFileTableIndex()
  145. {
  146. return m_selectedFileTableIndex;
  147. }
  148. AssetBundlerAbstractFileTableModel* SeedTabWidget::GetFileTableModel()
  149. {
  150. return m_fileTableModel.get();
  151. }
  152. void SeedTabWidget::SetActiveProjectLabel(const QString& labelText)
  153. {
  154. m_ui->activeProjectLabel->setText(labelText);
  155. }
  156. void SeedTabWidget::ApplyConfig()
  157. {
  158. const GUIApplicationManager::Config& config = m_guiApplicationManager->GetConfig();
  159. m_ui->fileTableFrame->setFixedWidth(config.fileTableWidth);
  160. m_ui->fileTableView->header()->resizeSection(SeedListFileTableModel::Column::ColumnFileName, config.seedListFileNameColumnWidth);
  161. m_ui->fileTableView->header()->resizeSection(SeedListFileTableModel::Column::ColumnCheckBox, config.checkBoxColumnWidth);
  162. m_ui->fileTableView->header()->resizeSection(SeedListFileTableModel::Column::ColumnProject, config.projectNameColumnWidth);
  163. m_ui->seedFileContentsTable->header()->resizeSection(
  164. SeedListTableModel::Column::ColumnRelativePath,
  165. config.seedListContentsNameColumnWidth);
  166. }
  167. void SeedTabWidget::UncheckSelectDefaultSeedListsCheckBox()
  168. {
  169. m_ui->selectDefaultSeedListsCheckBox->setChecked(false);
  170. }
  171. void SeedTabWidget::SetGenerateAssetListsButtonEnabled(bool isEnabled)
  172. {
  173. m_ui->generateAssetListsButton->setEnabled(isEnabled);
  174. }
  175. bool SeedTabWidget::OnPreError(
  176. const char* /*window*/,
  177. const char* /*fileName*/,
  178. int /*line*/,
  179. const char* /*func*/,
  180. const char* /*message*/)
  181. {
  182. m_hasWarningsOrErrors = true;
  183. return false;
  184. }
  185. bool SeedTabWidget::OnPreWarning(
  186. const char* /*window*/,
  187. const char* /*fileName*/,
  188. int /*line*/,
  189. const char* /*func*/,
  190. const char* /*message*/)
  191. {
  192. m_hasWarningsOrErrors = true;
  193. return false;
  194. }
  195. void SeedTabWidget::FileSelectionChanged(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/)
  196. {
  197. if (m_ui->fileTableView->selectionModel()->selectedRows().size() == 0)
  198. {
  199. // Set selected index to an invalid value
  200. m_selectedFileTableIndex = QModelIndex();
  201. m_ui->seedListFileAbsolutePathLabel->clear();
  202. return;
  203. }
  204. m_selectedFileTableIndex = m_fileTableFilterModel->mapToSource(m_ui->fileTableView->selectionModel()->currentIndex());
  205. m_seedListContentsModel = m_fileTableModel->GetSeedListFileContents(m_selectedFileTableIndex);
  206. m_seedListContentsFilterModel->setSourceModel(m_seedListContentsModel.data());
  207. m_ui->seedListFileAbsolutePathLabel->setText(m_fileTableModel->GetFileAbsolutePath(m_selectedFileTableIndex).c_str());
  208. }
  209. void SeedTabWidget::OnNewFileButtonPressed()
  210. {
  211. AZStd::string absoluteFilePath = NewFileDialog::OSNewFileDialog(
  212. this,
  213. AzToolsFramework::AssetSeedManager::GetSeedFileExtension(),
  214. "Seed List",
  215. m_guiApplicationManager->GetSeedListsFolder());
  216. if (absoluteFilePath.empty())
  217. {
  218. // User canceled out of the file dialog
  219. return;
  220. }
  221. AZStd::vector<AZStd::string> createdFiles = m_fileTableModel->CreateNewFiles(
  222. absoluteFilePath,
  223. AzFramework::PlatformFlags::Platform_NONE,
  224. QString(m_guiApplicationManager->GetCurrentProjectName().c_str()));
  225. if (!createdFiles.empty())
  226. {
  227. AddScanPathToAssetBundlerSettings(AssetBundlingFileType::SeedListFileType, createdFiles.at(0));
  228. }
  229. }
  230. void SeedTabWidget::OnSelectDefaultSeedListsCheckBoxChanged()
  231. {
  232. m_fileTableModel->SelectDefaultSeedLists(m_ui->selectDefaultSeedListsCheckBox->isChecked());
  233. }
  234. void SeedTabWidget::OnGenerateAssetListsButtonPressed()
  235. {
  236. m_generateAssetListsDialog.reset(
  237. new NewFileDialog(
  238. this,
  239. GenerateAssetListFilesDialogName,
  240. QString(m_guiApplicationManager->GetAssetListsFolder().c_str()),
  241. AzToolsFramework::AssetSeedManager::GetAssetListFileExtension(),
  242. QString(tr("Asset List (*.%1)")).arg(AzToolsFramework::AssetSeedManager::GetAssetListFileExtension()),
  243. m_guiApplicationManager->GetEnabledPlatforms()));
  244. auto dialogResponse = m_generateAssetListsDialog->exec();
  245. if (dialogResponse == QDialog::DialogCode::Rejected)
  246. {
  247. // User canceled the operation
  248. return;
  249. }
  250. m_hasWarningsOrErrors = false;
  251. auto createdFiles = m_fileTableModel->GenerateAssetLists(
  252. m_generateAssetListsDialog->GetAbsoluteFilePath(),
  253. m_generateAssetListsDialog->GetPlatformFlags());
  254. // Warnings will not prevent the generation of Asset List files, we must track them separately
  255. NewFileDialog::FileGenerationResultMessageBox(this, createdFiles, m_hasWarningsOrErrors);
  256. if (createdFiles.empty())
  257. {
  258. // Error has already been thrown
  259. return;
  260. }
  261. // Add created files to the file watcher
  262. for (const AZStd::string& absolutePath : createdFiles)
  263. {
  264. AddScanPathToAssetBundlerSettings(AssetBundlingFileType::AssetListFileType, absolutePath);
  265. }
  266. // The watched files list was updated after the files were created, so we need to force-reload them
  267. m_guiApplicationManager->UpdateFiles(AssetBundlingFileType::AssetListFileType, createdFiles);
  268. }
  269. void SeedTabWidget::OnEditSeedButtonPressed()
  270. {
  271. if (!m_selectedFileTableIndex.isValid())
  272. {
  273. AZ_Error(AssetBundler::AppWindowName, false, "Cannot perform Edit Seed operation: No Seed List File is selected");
  274. return;
  275. }
  276. // Get the current platforms of the selected Seed so we can display them as already checked
  277. QModelIndex currentSeedIndex =
  278. m_seedListContentsFilterModel->mapToSource(m_ui->seedFileContentsTable->selectionModel()->currentIndex());
  279. auto getPlatformOutcome = m_seedListContentsModel->GetSeedPlatforms(currentSeedIndex);
  280. if (!getPlatformOutcome.IsSuccess())
  281. {
  282. // Error has already been thrown
  283. return;
  284. }
  285. // Create and display the Edit Seed dialog
  286. m_editSeedDialog.reset(new EditSeedDialog(this, m_guiApplicationManager->GetEnabledPlatforms(), getPlatformOutcome.GetValue()));
  287. auto dialogResponse = m_editSeedDialog->exec();
  288. if (dialogResponse == QDialog::DialogCode::Rejected)
  289. {
  290. // User canceled the operation
  291. return;
  292. }
  293. // Set the data in the model
  294. m_fileTableModel->SetSeedPlatforms(m_selectedFileTableIndex, currentSeedIndex, m_editSeedDialog->GetPlatformFlags());
  295. }
  296. void SeedTabWidget::OnEditAllButtonPressed()
  297. {
  298. if (!m_selectedFileTableIndex.isValid())
  299. {
  300. AZ_Error(AssetBundler::AppWindowName, false, "Cannot perform Edit All operation: No Seed List File is selected");
  301. return;
  302. }
  303. // Get the platforms of all the seeds so we can display them as already checked or partially checked
  304. AzFramework::PlatformFlags selectedPlatformFlags = AzFramework::PlatformFlags::AllNamedPlatforms;
  305. AzFramework::PlatformFlags partiallySelectedPlatformFlags = AzFramework::PlatformFlags::Platform_NONE;
  306. QMap<QModelIndex, AzFramework::PlatformFlags> indexToPlatformFlagsMap;
  307. for (int row = 0; row < m_seedListContentsModel->rowCount(); ++row)
  308. {
  309. QModelIndex currentSeedIndex = m_seedListContentsModel->index(row, 0);
  310. auto getPlatformOutcome = m_seedListContentsModel->GetSeedPlatforms(currentSeedIndex);
  311. if (!getPlatformOutcome.IsSuccess())
  312. {
  313. // Error has already been thrown
  314. return;
  315. }
  316. indexToPlatformFlagsMap[currentSeedIndex] = getPlatformOutcome.TakeValue();
  317. selectedPlatformFlags &= indexToPlatformFlagsMap[currentSeedIndex];
  318. partiallySelectedPlatformFlags |= selectedPlatformFlags ^ indexToPlatformFlagsMap[currentSeedIndex];
  319. }
  320. // Create and display the Edit Seed dialog
  321. m_editSeedDialog.reset(new EditSeedDialog(
  322. this,
  323. m_guiApplicationManager->GetEnabledPlatforms(),
  324. selectedPlatformFlags,
  325. partiallySelectedPlatformFlags));
  326. auto dialogResponse = m_editSeedDialog->exec();
  327. if (dialogResponse == QDialog::DialogCode::Rejected)
  328. {
  329. // User canceled the operation
  330. return;
  331. }
  332. // Set the data in the model
  333. for (int row = 0; row < m_seedListContentsModel->rowCount(); ++row)
  334. {
  335. QModelIndex currentSeedIndex = m_seedListContentsModel->index(row, 0);
  336. AzFramework::PlatformFlags checkedPlatforms = m_editSeedDialog->GetPlatformFlags();
  337. AzFramework::PlatformFlags partiallyCheckedPlatforms = m_editSeedDialog->GetPartiallySelectedPlatformFlags();
  338. // If the platform is partially checked, we want to keep its original status when saving the changes
  339. AzFramework::PlatformFlags platformFlags =
  340. indexToPlatformFlagsMap[currentSeedIndex] & partiallyCheckedPlatforms | checkedPlatforms;
  341. m_fileTableModel->SetSeedPlatforms(m_selectedFileTableIndex, currentSeedIndex, platformFlags);
  342. }
  343. }
  344. void SeedTabWidget::OnAddSeedButtonPressed()
  345. {
  346. if (!m_selectedFileTableIndex.isValid())
  347. {
  348. AZ_Error(AssetBundler::AppWindowName, false, "Cannot perform Add Seed operation: No Seed List File is selected");
  349. return;
  350. }
  351. // Get path to the platform-specific cache folder of one of the enabled platforms
  352. AzFramework::PlatformFlags enabledPlatforms = m_guiApplicationManager->GetEnabledPlatforms();
  353. AZStd::fixed_vector<AzFramework::PlatformId, AzFramework::PlatformId::NumPlatformIds> enabledPlatformIndices =
  354. AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(enabledPlatforms);
  355. AZStd::string platformSpecificCachePath =
  356. AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(enabledPlatformIndices[0]);
  357. // Create and display the Add Seed Dialog
  358. m_addSeedDialog.reset(new AddSeedDialog(this, enabledPlatforms, platformSpecificCachePath));
  359. auto dialogResponse = m_addSeedDialog->exec();
  360. if (dialogResponse == QDialog::DialogCode::Rejected)
  361. {
  362. // User canceled the operation
  363. return;
  364. }
  365. // Set the data in the model
  366. m_fileTableModel->AddSeed(m_selectedFileTableIndex, m_addSeedDialog->GetFileName(), m_addSeedDialog->GetPlatformFlags());
  367. }
  368. void SeedTabWidget::OnRemoveSeedButtonPressed()
  369. {
  370. if (!m_selectedFileTableIndex.isValid())
  371. {
  372. AZ_Error(AssetBundler::AppWindowName, false, "Cannot perform Remove Seed operation: No Seed List File is selected");
  373. return;
  374. }
  375. // Set the data in the model
  376. QModelIndex currentSeedIndex =
  377. m_seedListContentsFilterModel->mapToSource(m_ui->seedFileContentsTable->selectionModel()->currentIndex());
  378. m_fileTableModel->RemoveSeed(m_selectedFileTableIndex, currentSeedIndex);
  379. }
  380. void SeedTabWidget::OnSeedListContentsTableContextMenuRequested(const QPoint& pos)
  381. {
  382. if (!m_selectedFileTableIndex.isValid())
  383. {
  384. return;
  385. }
  386. QMenu* contextMenu = new QMenu(this);
  387. contextMenu->setToolTipsVisible(true);
  388. QAction* action = contextMenu->addAction(tr("Edit Platforms"));
  389. connect(action, &QAction::triggered, this, &SeedTabWidget::OnEditSeedButtonPressed);
  390. action->setToolTip(tr("Change what platforms are referenced when generating an Asset List file."));
  391. contextMenu->addSeparator();
  392. action = contextMenu->addAction(tr("Add Seed"));
  393. connect(action, &QAction::triggered, this, &SeedTabWidget::OnAddSeedButtonPressed);
  394. action->setToolTip(tr("Add a new Seed to the Seed List file."));
  395. action = contextMenu->addAction(tr("Remove Seed"));
  396. connect(action, &QAction::triggered, this, &SeedTabWidget::OnRemoveSeedButtonPressed);
  397. action->setToolTip(tr("Removes the Seed from the Seed List file."));
  398. contextMenu->exec(m_ui->seedFileContentsTable->mapToGlobal(pos));
  399. delete contextMenu;
  400. }
  401. } //namespace AssetBundler
  402. #include <source/ui/moc_SeedTabWidget.cpp>