2
0

RulesTabWidget.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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/RulesTabWidget.h>
  9. #include <source/ui/ui_RulesTabWidget.h>
  10. #include <source/models/RulesFileTableModel.h>
  11. #include <source/ui/ComparisonDataWidget.h>
  12. #include <source/ui/NewFileDialog.h>
  13. #include <source/utils/GUIApplicationManager.h>
  14. #include <source/utils/utils.h>
  15. #include <AzCore/Outcome/Outcome.h>
  16. #include <AzFramework/StringFunc/StringFunc.h>
  17. #include <AzToolsFramework/Asset/AssetBundler.h>
  18. #include <AzToolsFramework/AssetBundle/AssetBundleAPI.h>
  19. #include <AzQtComponents/Components/FilteredSearchWidget.h>
  20. #include <QAbstractTableModel>
  21. #include <QItemSelectionModel>
  22. #include <QMenu>
  23. #include <QPushButton>
  24. namespace AssetBundler
  25. {
  26. RulesTabWidget::RulesTabWidget(QWidget* parent, GUIApplicationManager* guiApplicationManager)
  27. : AssetBundlerTabWidget(parent, guiApplicationManager)
  28. {
  29. m_ui.reset(new Ui::RulesTabWidget);
  30. m_ui->setupUi(this);
  31. m_ui->mainVerticalLayout->setContentsMargins(MarginSize, MarginSize, MarginSize, MarginSize);
  32. m_fileTableModel.reset(new RulesFileTableModel);
  33. m_ui->fileTableView->setModel(m_fileTableModel.data());
  34. // Table View of all Rules files
  35. m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel(
  36. this,
  37. m_fileTableModel->GetFileNameColumnIndex(),
  38. m_fileTableModel->GetTimeStampColumnIndex()));
  39. m_fileTableFilterModel->setSourceModel(m_fileTableModel.data());
  40. m_ui->fileTableView->setModel(m_fileTableFilterModel.data());
  41. connect(m_ui->fileFilteredSearchWidget,
  42. &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
  43. m_fileTableFilterModel.data(),
  44. static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetBundlerFileTableFilterModel::FilterChanged));
  45. connect(m_ui->fileTableView->selectionModel(),
  46. &QItemSelectionModel::selectionChanged,
  47. this,
  48. &RulesTabWidget::FileSelectionChanged);
  49. m_ui->fileTableView->setIndentation(0);
  50. // New File Button
  51. connect(m_ui->createNewFileButton, &QPushButton::clicked, this, &RulesTabWidget::OnNewFileButtonPressed);
  52. // Run Rule Button
  53. m_ui->runRuleButton->setEnabled(false);
  54. connect(m_ui->runRuleButton, &QPushButton::clicked, this, &RulesTabWidget::OnRunRuleButtonPressed);
  55. // Add Comparison Step button
  56. connect(m_ui->addComparisonStepButton, &QPushButton::clicked, this, &RulesTabWidget::AddNewComparisonStep);
  57. SetModelDataSource();
  58. }
  59. bool RulesTabWidget::HasUnsavedChanges()
  60. {
  61. return m_fileTableModel->HasUnsavedChanges();
  62. }
  63. void RulesTabWidget::Reload()
  64. {
  65. m_fileTableModel->Reload(
  66. AzToolsFramework::AssetFileInfoListComparison::GetComparisonRulesFileExtension(),
  67. m_watchedFolders,
  68. m_watchedFiles);
  69. FileSelectionChanged();
  70. }
  71. bool RulesTabWidget::SaveCurrentSelection()
  72. {
  73. return m_fileTableModel->Save(m_selectedFileTableIndex);
  74. }
  75. bool RulesTabWidget::SaveAll()
  76. {
  77. return m_fileTableModel->SaveAll();
  78. }
  79. void RulesTabWidget::SetModelDataSource()
  80. {
  81. // Remove the current watched folders and files
  82. m_guiApplicationManager->RemoveWatchedPaths(m_watchedFolders + m_watchedFiles);
  83. // Set the new watched folder for the model
  84. m_watchedFolders.clear();
  85. m_watchedFiles.clear();
  86. m_watchedFolders.insert(m_guiApplicationManager->GetRulesFolder().c_str());
  87. ReadScanPathsFromAssetBundlerSettings(AssetBundlingFileType::RulesFileType);
  88. m_guiApplicationManager->AddWatchedPaths(m_watchedFolders + m_watchedFiles);
  89. }
  90. AzQtComponents::TableView* RulesTabWidget::GetFileTableView()
  91. {
  92. return m_ui->fileTableView;
  93. }
  94. QModelIndex RulesTabWidget::GetSelectedFileTableIndex()
  95. {
  96. return m_selectedFileTableIndex;
  97. }
  98. AssetBundlerAbstractFileTableModel* RulesTabWidget::GetFileTableModel()
  99. {
  100. return m_fileTableModel.get();
  101. }
  102. void RulesTabWidget::SetActiveProjectLabel(const QString& labelText)
  103. {
  104. m_ui->activeProjectLabel->setText(labelText);
  105. }
  106. void RulesTabWidget::ApplyConfig()
  107. {
  108. const GUIApplicationManager::Config& config = m_guiApplicationManager->GetConfig();
  109. m_ui->fileTableFrame->setFixedWidth(config.fileTableWidth);
  110. m_ui->fileTableView->header()->resizeSection(RulesFileTableModel::Column::ColumnFileName, config.fileNameColumnWidth);
  111. }
  112. void RulesTabWidget::FileSelectionChanged(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/)
  113. {
  114. if (m_ui->fileTableView->selectionModel()->selectedRows().size() == 0)
  115. {
  116. m_selectedFileTableIndex = QModelIndex();
  117. m_selectedComparisonRules = nullptr;
  118. m_ui->runRuleButton->setEnabled(false);
  119. m_ui->addComparisonStepButton->setEnabled(false);
  120. m_ui->rulesFileAbsolutePathLabel->clear();
  121. RemoveAllComparisonDataCards();
  122. return;
  123. }
  124. m_ui->runRuleButton->setEnabled(true);
  125. m_ui->addComparisonStepButton->setEnabled(true);
  126. m_selectedFileTableIndex = m_fileTableFilterModel->mapToSource(m_ui->fileTableView->selectionModel()->selectedRows()[0]);
  127. m_selectedComparisonRules = m_fileTableModel->GetComparisonSteps(m_selectedFileTableIndex);
  128. m_ui->rulesFileAbsolutePathLabel->setText(m_fileTableModel->GetFileAbsolutePath(m_selectedFileTableIndex).c_str());
  129. RebuildComparisonDataCardList();
  130. }
  131. void RulesTabWidget::OnNewFileButtonPressed()
  132. {
  133. AZStd::string absoluteFilePath = NewFileDialog::OSNewFileDialog(
  134. this,
  135. AzToolsFramework::AssetFileInfoListComparison::GetComparisonRulesFileExtension(),
  136. "Comparison Rules",
  137. m_guiApplicationManager->GetRulesFolder());
  138. if (absoluteFilePath.empty())
  139. {
  140. // User canceled out of the dialog
  141. return;
  142. }
  143. AZStd::vector<AZStd::string> createdFile = m_fileTableModel->CreateNewFiles(absoluteFilePath);
  144. if (!createdFile.empty())
  145. {
  146. AddScanPathToAssetBundlerSettings(AssetBundlingFileType::RulesFileType, createdFile.at(0));
  147. }
  148. }
  149. void RulesTabWidget::OnRunRuleButtonPressed()
  150. {
  151. using namespace AzToolsFramework;
  152. // Determine which platforms all of the input Asset List files have in common
  153. AzFramework::PlatformFlags commonPlatforms = AzFramework::PlatformFlags::AllNamedPlatforms;
  154. for (const AssetFileInfoListComparison::ComparisonData& comparisonStep : m_selectedComparisonRules->GetComparisonList())
  155. {
  156. if (!comparisonStep.m_cachedFirstInputPath.empty())
  157. {
  158. commonPlatforms = commonPlatforms & GetPlatformsOnDiskForPlatformSpecificFile(comparisonStep.m_cachedFirstInputPath);
  159. }
  160. if (!comparisonStep.m_cachedSecondInputPath.empty())
  161. {
  162. commonPlatforms = commonPlatforms & GetPlatformsOnDiskForPlatformSpecificFile(comparisonStep.m_cachedSecondInputPath);
  163. }
  164. }
  165. if (commonPlatforms == AzFramework::PlatformFlags::Platform_NONE)
  166. {
  167. AZ_Error("AssetBundler", false, "Unable to run Rule: Input Asset List files have no platforms in common.");
  168. return;
  169. }
  170. // Prompt the user to select an output path and the platforms to run the rule on
  171. NewFileDialog runRuleDialog = NewFileDialog(
  172. this,
  173. "Run Rule",
  174. QString(m_guiApplicationManager->GetAssetListsFolder().c_str()),
  175. AzToolsFramework::AssetSeedManager::GetAssetListFileExtension(),
  176. QString(tr("Asset List (*.%1)")).arg(AzToolsFramework::AssetSeedManager::GetAssetListFileExtension()),
  177. commonPlatforms,
  178. true);
  179. auto dialogResponse = runRuleDialog.exec();
  180. if (dialogResponse == QDialog::DialogCode::Rejected)
  181. {
  182. // User canceled the operation
  183. return;
  184. }
  185. AZStd::vector<AZStd::string> outputFilePaths;
  186. bool hasFileGenerationErrors = false;
  187. AZStd::fixed_vector<AZStd::string, AzFramework::NumPlatforms> selectedPlatformNames{ AZStd::from_range,
  188. AzFramework::PlatformHelper::GetPlatforms(runRuleDialog.GetPlatformFlags()) };
  189. for (const AZStd::string& platformName : selectedPlatformNames)
  190. {
  191. // We do not want to modify the original Rules file, as we do not save Asset List file paths to disk
  192. AssetFileInfoListComparison currentFileCopy = *m_selectedComparisonRules.get();
  193. //Update the first and second input values with any non-token Asset List file paths that have been set,
  194. size_t numComparisonSteps = currentFileCopy.GetNumComparisonSteps();
  195. for (size_t comparisonStepIndex = 0; comparisonStepIndex < numComparisonSteps; ++comparisonStepIndex)
  196. {
  197. AssetFileInfoListComparison::ComparisonData comparisonStep = currentFileCopy.GetComparisonList()[comparisonStepIndex];
  198. if (comparisonStep.m_firstInput.empty())
  199. {
  200. if (comparisonStep.m_cachedFirstInputPath.empty())
  201. {
  202. AZ_Error("AssetBundler", false,
  203. "Unable to run Rule: Comparison Step #%u has no specified first input.", comparisonStepIndex);
  204. return;
  205. }
  206. FilePath firstInput = FilePath(comparisonStep.m_cachedFirstInputPath, platformName);
  207. currentFileCopy.SetFirstInput(comparisonStepIndex, firstInput.AbsolutePath());
  208. }
  209. if (comparisonStep.m_comparisonType != AssetFileInfoListComparison::ComparisonType::FilePattern
  210. && comparisonStep.m_secondInput.empty())
  211. {
  212. if (comparisonStep.m_cachedSecondInputPath.empty())
  213. {
  214. AZ_Error("AssetBundler", false,
  215. "Unable to run Rule: Comparison Step #%u has no specified second input.", comparisonStepIndex);
  216. return;
  217. }
  218. FilePath secondInput = FilePath(comparisonStep.m_cachedSecondInputPath, platformName);
  219. currentFileCopy.SetSecondInput(comparisonStepIndex, secondInput.AbsolutePath());
  220. }
  221. }
  222. // Set the output location of the Asset List file that will be generated
  223. FilePath finalOutputPath = FilePath(runRuleDialog.GetAbsoluteFilePath(), platformName);
  224. currentFileCopy.SetOutput(numComparisonSteps - 1, finalOutputPath.AbsolutePath());
  225. //Run the Rule
  226. auto compareOutcome = currentFileCopy.CompareAndSaveResults();
  227. if (compareOutcome.IsSuccess())
  228. {
  229. outputFilePaths.emplace_back(finalOutputPath.AbsolutePath());
  230. }
  231. else
  232. {
  233. hasFileGenerationErrors = true;
  234. AZ_Error("AssetBundler", false, compareOutcome.GetError().c_str());
  235. }
  236. }
  237. // Add created files to the file watcher
  238. for (const AZStd::string& absolutePath : outputFilePaths)
  239. {
  240. AddScanPathToAssetBundlerSettings(AssetBundlingFileType::AssetListFileType, absolutePath);
  241. }
  242. // The watched files list was updated after the files were created, so we need to force-reload them
  243. m_guiApplicationManager->UpdateFiles(AssetBundlingFileType::AssetListFileType, outputFilePaths);
  244. NewFileDialog::FileGenerationResultMessageBox(this, outputFilePaths, hasFileGenerationErrors);
  245. }
  246. void RulesTabWidget::MarkFileChanged()
  247. {
  248. m_fileTableModel->MarkFileChanged(m_selectedFileTableIndex);
  249. }
  250. void RulesTabWidget::RebuildComparisonDataCardList()
  251. {
  252. RemoveAllComparisonDataCards();
  253. PopulateComparisonDataCardList();
  254. }
  255. void RulesTabWidget::PopulateComparisonDataCardList()
  256. {
  257. if (!m_selectedComparisonRules)
  258. {
  259. return;
  260. }
  261. for (int index = 0; index < m_selectedComparisonRules->GetComparisonList().size(); ++index)
  262. {
  263. CreateComparisonDataCard(m_selectedComparisonRules, static_cast<size_t>(index));
  264. }
  265. }
  266. void RulesTabWidget::CreateComparisonDataCard(
  267. AZStd::shared_ptr<AzToolsFramework::AssetFileInfoListComparison> comparisonList,
  268. size_t comparisonDataIndex)
  269. {
  270. ComparisonDataCard* comparisonDataCard = new ComparisonDataCard(
  271. comparisonList,
  272. comparisonDataIndex,
  273. m_guiApplicationManager->GetAssetListsFolder());
  274. comparisonDataCard->setTitle(tr("Step %1").arg(static_cast<int>(comparisonDataIndex) + 1));
  275. m_ui->comparisonDataListLayout->addWidget(comparisonDataCard);
  276. m_comparisonDataCardList.push_back(comparisonDataCard);
  277. ComparisonDataWidget* comparisonDataWidget = comparisonDataCard->GetComparisonDataWidget();
  278. connect(comparisonDataCard,
  279. &ComparisonDataCard::comparisonDataCardContextMenuRequested,
  280. this,
  281. &RulesTabWidget::OnComparisonDataCardContextMenuRequested);
  282. connect(comparisonDataWidget, &ComparisonDataWidget::comparisonDataChanged, this, &RulesTabWidget::MarkFileChanged);
  283. connect(comparisonDataWidget,
  284. &ComparisonDataWidget::comparisonDataTokenNameChanged,
  285. this,
  286. &RulesTabWidget::OnAnyTokenNameChanged);
  287. comparisonDataCard->show();
  288. }
  289. void RulesTabWidget::RemoveAllComparisonDataCards()
  290. {
  291. m_comparisonDataCardList.clear();
  292. while (!m_ui->comparisonDataListLayout->isEmpty())
  293. {
  294. QLayoutItem* item = m_ui->comparisonDataListLayout->takeAt(0);
  295. item->widget()->hide();
  296. delete item;
  297. }
  298. }
  299. void RulesTabWidget::AddNewComparisonStep()
  300. {
  301. if (!m_selectedComparisonRules)
  302. {
  303. return;
  304. }
  305. if (!m_selectedComparisonRules->AddComparisonStep(AzToolsFramework::AssetFileInfoListComparison::ComparisonData()))
  306. {
  307. return;
  308. }
  309. CreateComparisonDataCard(m_selectedComparisonRules, m_selectedComparisonRules->GetComparisonList().size() - 1);
  310. MarkFileChanged();
  311. }
  312. void RulesTabWidget::RemoveComparisonStep(size_t comparisonDataIndex)
  313. {
  314. if (!m_selectedComparisonRules)
  315. {
  316. return;
  317. }
  318. if (m_selectedComparisonRules->RemoveComparisonStep(comparisonDataIndex))
  319. {
  320. MarkFileChanged();
  321. RebuildComparisonDataCardList();
  322. }
  323. }
  324. void RulesTabWidget::MoveComparisonStep(size_t startingIndex, size_t destinationIndex)
  325. {
  326. if (!m_selectedComparisonRules)
  327. {
  328. return;
  329. }
  330. if (m_selectedComparisonRules->MoveComparisonStep(startingIndex, destinationIndex))
  331. {
  332. MarkFileChanged();
  333. RebuildComparisonDataCardList();
  334. }
  335. }
  336. void RulesTabWidget::OnAnyTokenNameChanged(size_t comparisonDataIndex)
  337. {
  338. if (comparisonDataIndex >= m_comparisonDataCardList.size() - 1)
  339. {
  340. return;
  341. }
  342. for (size_t i = comparisonDataIndex + 1; i < m_comparisonDataCardList.size(); ++i)
  343. {
  344. m_comparisonDataCardList[i]->GetComparisonDataWidget()->UpdateListOfTokenNames();
  345. }
  346. }
  347. void RulesTabWidget::OnComparisonDataCardContextMenuRequested(size_t comparisonDataIndex, const QPoint& position)
  348. {
  349. if (!m_selectedComparisonRules)
  350. {
  351. return;
  352. }
  353. QMenu menu;
  354. QAction* moveUpAction = new QAction(tr("Move Up"), this);
  355. moveUpAction->setEnabled(comparisonDataIndex > 0);
  356. connect(moveUpAction, &QAction::triggered, this, [=, this]() { MoveComparisonStep(comparisonDataIndex, comparisonDataIndex - 1); });
  357. menu.addAction(moveUpAction);
  358. QAction* moveDownAction = new QAction(tr("Move Down"), this);
  359. moveDownAction->setEnabled(comparisonDataIndex < m_selectedComparisonRules->GetNumComparisonSteps() - 1);
  360. connect(moveDownAction, &QAction::triggered, this, [=, this]() { MoveComparisonStep(comparisonDataIndex, comparisonDataIndex + 2); });
  361. menu.addAction(moveDownAction);
  362. QAction* separator = new QAction(this);
  363. separator->setSeparator(true);
  364. menu.addAction(separator);
  365. QAction* deleteAction = new QAction(tr("Remove Comparison Step"), this);
  366. connect(deleteAction, &QAction::triggered, this, [=, this]() { RemoveComparisonStep(comparisonDataIndex); });
  367. menu.addAction(deleteAction);
  368. menu.exec(position);
  369. }
  370. } // namespace AssetBundler
  371. #include <source/ui/moc_RulesTabWidget.cpp>