GUIApplicationManager.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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/utils/GUIApplicationManager.h>
  9. #include <source/ui/MainWindow.h>
  10. #include <source/utils/utils.h>
  11. #include <AzCore/IO/FileIO.h>
  12. #include <AzCore/Utils/Utils.h>
  13. #include <AzFramework/Asset/AssetCatalog.h>
  14. #include <AzFramework/StringFunc/StringFunc.h>
  15. #include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalogBus.h>
  16. #include <AzQtComponents/Components/ConfigHelpers.h>
  17. #include <AzQtComponents/Components/StyleManager.h>
  18. #include <AzQtComponents/Components/WindowDecorationWrapper.h>
  19. #include <QApplication>
  20. #include <QLocale>
  21. #include <QStringList>
  22. // Forward declare platform-specific functions
  23. namespace Platform
  24. {
  25. /// On Windows this will return the setting for our custom title bar
  26. /// On other platforms this will return the setting for using platform default
  27. /// This ensures that functions like Exit, Maximize, and Minimize appear in the right platform-specific style
  28. AzQtComponents::WindowDecorationWrapper::Option GetWindowDecorationWrapperOption();
  29. } // namespace Platform
  30. const char AssetBundlingFolderName [] = "AssetBundling";
  31. const char SeedListsFolderName [] = "SeedLists";
  32. const char AssetListsFolderName [] = "AssetLists";
  33. const char RulesFolderName[] = "Rules";
  34. const char BundleSettingsFolderName [] = "BundleSettings";
  35. const char BundlesFolderName [] = "Bundles";
  36. namespace AssetBundler
  37. {
  38. GUIApplicationManager::Config GUIApplicationManager::loadConfig(QSettings& settings)
  39. {
  40. using namespace AzQtComponents;
  41. Config config = defaultConfig();
  42. // Error Log
  43. {
  44. ConfigHelpers::GroupGuard details(&settings, QStringLiteral("ErrorLogDetails"));
  45. ConfigHelpers::read<int>(settings, QStringLiteral("LogTypeColumnWidth"), config.logTypeColumnWidth);
  46. ConfigHelpers::read<int>(settings, QStringLiteral("LogSourceColumnWidth"), config.logSourceColumnWidth);
  47. }
  48. // General File Tables
  49. {
  50. ConfigHelpers::GroupGuard details(&settings, QStringLiteral("GeneralFileTableDetails"));
  51. ConfigHelpers::read<int>(settings, QStringLiteral("FileTableWidth"), config.fileTableWidth);
  52. ConfigHelpers::read<int>(settings, QStringLiteral("FileNameColumnWidth"), config.fileNameColumnWidth);
  53. }
  54. // Seeds Tab
  55. {
  56. ConfigHelpers::GroupGuard details(&settings, QStringLiteral("SeedsTabDetails"));
  57. ConfigHelpers::read<int>(settings, QStringLiteral("CheckBoxColumnWidth"), config.checkBoxColumnWidth);
  58. ConfigHelpers::read<int>(settings, QStringLiteral("SeedListFileNameColumnWidth"), config.seedListFileNameColumnWidth);
  59. ConfigHelpers::read<int>(settings, QStringLiteral("ProjectNameColumnWidth"), config.projectNameColumnWidth);
  60. ConfigHelpers::read<int>(settings, QStringLiteral("SeedListContentsNameColumnWidth"), config.seedListContentsNameColumnWidth);
  61. }
  62. // Asset Lists Tab
  63. {
  64. ConfigHelpers::GroupGuard details(&settings, QStringLiteral("AssetListsTabDetails"));
  65. ConfigHelpers::read<int>(settings, QStringLiteral("AssetListFileNameColumnWidth"), config.assetListFileNameColumnWidth);
  66. ConfigHelpers::read<int>(settings, QStringLiteral("AssetListPlatformColumnWidth"), config.assetListPlatformColumnWidth);
  67. ConfigHelpers::read<int>(settings, QStringLiteral("ProductAssetNameColumnWidth"), config.productAssetNameColumnWidth);
  68. ConfigHelpers::read<int>(
  69. settings, QStringLiteral("ProductAssetRelativePathColumnWidth"), config.productAssetRelativePathColumnWidth);
  70. }
  71. return config;
  72. }
  73. GUIApplicationManager::Config GUIApplicationManager::defaultConfig()
  74. {
  75. // These are used if the values can't be read from AssetBundlerConfig.ini.
  76. Config config;
  77. config.logTypeColumnWidth = 150;
  78. config.logSourceColumnWidth = 150;
  79. config.fileTableWidth = 250;
  80. config.fileNameColumnWidth = 150;
  81. config.checkBoxColumnWidth = 150;
  82. config.seedListFileNameColumnWidth = 150;
  83. config.projectNameColumnWidth = 150;
  84. config.seedListContentsNameColumnWidth = 150;
  85. config.assetListFileNameColumnWidth = 150;
  86. config.assetListPlatformColumnWidth = 150;
  87. config.productAssetNameColumnWidth = 150;
  88. config.productAssetRelativePathColumnWidth = 150;
  89. return config;
  90. }
  91. GUIApplicationManager::GUIApplicationManager(int* argc, char*** argv, QObject* parent)
  92. : ApplicationManager(argc, argv, parent)
  93. {
  94. }
  95. GUIApplicationManager::~GUIApplicationManager()
  96. {
  97. // Reset this before DestroyApplication, BusDisconnect needs to happen before Application::Stop() destroys the allocators.
  98. m_platformCatalogManager.reset();
  99. }
  100. bool GUIApplicationManager::Init()
  101. {
  102. m_isInitializing = true;
  103. // Initialize Asset Bundler Batch
  104. ApplicationManager::Init();
  105. AZ::IO::FixedMaxPath engineRoot = GetEngineRoot();
  106. if (engineRoot.empty())
  107. {
  108. // Error has already been thrown
  109. return false;
  110. }
  111. // Determine the name of the current project
  112. auto projectOutcome = AssetBundler::GetCurrentProjectName();
  113. if (!projectOutcome.IsSuccess())
  114. {
  115. AZ_Error("AssetBundler", false, projectOutcome.GetError().c_str());
  116. return false;
  117. }
  118. m_currentProjectName = projectOutcome.GetValue();
  119. // Set up paths to the Project folder, Project Cache folder, and determine enabled platforms
  120. auto pathOutcome = InitializePaths();
  121. if (!pathOutcome.IsSuccess())
  122. {
  123. AZ_Error("AssetBundler", false, pathOutcome.GetError().c_str());
  124. return false;
  125. }
  126. // Set up platform-specific Asset Catalogs
  127. m_platformCatalogManager = AZStd::make_unique<AzToolsFramework::PlatformAddressedAssetCatalogManager>();
  128. // Define some application-level settings
  129. QApplication::setOrganizationName("O3DE");
  130. QApplication::setApplicationName("Asset Bundler");
  131. QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates));
  132. QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
  133. QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
  134. m_isInitializing = false;
  135. // Create the actual Qt Application
  136. m_qApp.reset(new QApplication(*GetArgC(), *GetArgV()));
  137. // Create the Main Window
  138. m_mainWindow.reset(new MainWindow(this));
  139. return true;
  140. }
  141. bool GUIApplicationManager::Run()
  142. {
  143. // Set up the Style Manager
  144. AzQtComponents::StyleManager styleManager(qApp);
  145. styleManager.initialize(qApp, GetEngineRoot());
  146. AZ::IO::FixedMaxPath engineRoot(GetEngineRoot());
  147. QDir engineRootDir(engineRoot.c_str());
  148. AzQtComponents::StyleManager::addSearchPaths(
  149. QStringLiteral("style"),
  150. engineRootDir.filePath(QStringLiteral("Code/Tools/AssetBundler/source/ui/style")),
  151. QStringLiteral(":/AssetBundler/style"),
  152. engineRoot);
  153. AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:AssetBundler.qss"));
  154. AzQtComponents::ConfigHelpers::loadConfig<Config, GUIApplicationManager>(
  155. &m_fileWatcher,
  156. &m_config,
  157. QStringLiteral("style:AssetBundlerConfig.ini"),
  158. this,
  159. std::bind(&GUIApplicationManager::ApplyConfig, this));
  160. ApplyConfig();
  161. qApp->setWindowIcon(QIcon("style:[email protected]"));
  162. // Set up the Main Window
  163. auto wrapper = new AzQtComponents::WindowDecorationWrapper(Platform::GetWindowDecorationWrapperOption());
  164. wrapper->setGuest(m_mainWindow.data());
  165. m_mainWindow->Activate();
  166. wrapper->show();
  167. m_mainWindow->show();
  168. qApp->setQuitOnLastWindowClosed(true);
  169. // Run the application
  170. return qApp->exec();
  171. }
  172. void GUIApplicationManager::AddWatchedPath(const QString& folderPath)
  173. {
  174. m_fileWatcher.addPath(folderPath);
  175. }
  176. void GUIApplicationManager::AddWatchedPaths(const QSet<QString>& folderPaths)
  177. {
  178. m_fileWatcher.addPaths(folderPaths.values());
  179. }
  180. void GUIApplicationManager::RemoveWatchedPath(const QString& path)
  181. {
  182. m_fileWatcher.removePath(path);
  183. }
  184. void GUIApplicationManager::RemoveWatchedPaths(const QSet<QString>& paths)
  185. {
  186. // Check whether the list is empty to get rid of the warning from Qt
  187. if (paths.isEmpty())
  188. {
  189. return;
  190. }
  191. m_fileWatcher.removePaths(paths.values());
  192. }
  193. bool GUIApplicationManager::OnPreError(
  194. const char* /*window*/,
  195. const char* /*fileName*/,
  196. int /*line*/,
  197. const char* /*func*/,
  198. const char* message)
  199. {
  200. // We want to display errors during initialization, then let the MainWindow handle errors during runtime
  201. if (m_isInitializing)
  202. {
  203. // These are fatal initialization errors, and the application will shut down after the user closes the message box
  204. m_qApp.reset(new QApplication(*GetArgC(), *GetArgV()));
  205. QMessageBox errorMessageBox;
  206. errorMessageBox.setWindowTitle("Asset Bundler");
  207. errorMessageBox.setText(message);
  208. errorMessageBox.setStandardButtons(QMessageBox::Ok);
  209. errorMessageBox.setDefaultButton(QMessageBox::Ok);
  210. errorMessageBox.exec();
  211. return true;
  212. }
  213. return false;
  214. }
  215. bool GUIApplicationManager::OnPreWarning(
  216. const char* /*window*/,
  217. const char* /*fileName*/,
  218. int /*line*/,
  219. const char* /*func*/,
  220. const char* /*message*/)
  221. {
  222. // Don't handle warnings, let the MainWindow print them
  223. return false;
  224. }
  225. bool GUIApplicationManager::OnPrintf(const char* /*window*/, const char* /*message*/)
  226. {
  227. // This is disabled during initialization to prevent a lot of message spam printed to the CLI that gets generated on setup
  228. return m_isInitializing;
  229. }
  230. AZ::Outcome<void, AZStd::string> GUIApplicationManager::InitializePaths()
  231. {
  232. // Calculate the path to the Cache for the current project
  233. auto pathOutcome = AssetBundler::GetProjectCacheFolderPath();
  234. if (!pathOutcome.IsSuccess())
  235. {
  236. return AZ::Failure(pathOutcome.GetError());
  237. }
  238. m_currentProjectCacheFolder = pathOutcome.GetValue().String();
  239. // Calculate the path to the current project folder
  240. pathOutcome = AssetBundler::GetProjectFolderPath();
  241. if (!pathOutcome.IsSuccess())
  242. {
  243. return AZ::Failure(pathOutcome.GetError());
  244. }
  245. m_currentProjectFolder = pathOutcome.GetValue().String();
  246. // Generate the AssetBundling folder inside the current project
  247. AzFramework::StringFunc::Path::ConstructFull(m_currentProjectFolder.c_str(), AssetBundlingFolderName, m_assetBundlingFolder);
  248. // Seed Lists folder
  249. AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), SeedListsFolderName, m_seedListsFolder);
  250. AZ::Outcome<void, AZStd::string> createPathOutcome = AssetBundler::MakePath(m_seedListsFolder);
  251. if (!createPathOutcome.IsSuccess())
  252. {
  253. return createPathOutcome;
  254. }
  255. // Asset Lists folder
  256. AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), AssetListsFolderName, m_assetListsFolder);
  257. createPathOutcome = AssetBundler::MakePath(m_assetListsFolder);
  258. if (!createPathOutcome.IsSuccess())
  259. {
  260. return createPathOutcome;
  261. }
  262. // Rules folder
  263. AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), RulesFolderName, m_rulesFolder);
  264. createPathOutcome = AssetBundler::MakePath(m_rulesFolder);
  265. if (!createPathOutcome.IsSuccess())
  266. {
  267. return createPathOutcome;
  268. }
  269. // Bundle Settings Folder
  270. AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), BundleSettingsFolderName, m_bundleSettingsFolder);
  271. createPathOutcome = AssetBundler::MakePath(m_bundleSettingsFolder);
  272. if (!createPathOutcome.IsSuccess())
  273. {
  274. return createPathOutcome;
  275. }
  276. // Bundles Folder
  277. AzFramework::StringFunc::Path::ConstructFull(m_assetBundlingFolder.c_str(), BundlesFolderName, m_bundlesFolder);
  278. createPathOutcome = AssetBundler::MakePath(m_bundlesFolder);
  279. if (!createPathOutcome.IsSuccess())
  280. {
  281. return createPathOutcome;
  282. }
  283. // Determine the enabled platforms
  284. m_enabledPlatforms = GetEnabledPlatformFlags(GetEngineRoot(), AZStd::string_view(AZ::Utils::GetProjectPath()));
  285. // Determine which Gems are enabled for the current project
  286. if (!AzFramework::GetGemsInfo(m_gemInfoList, *m_settingsRegistry))
  287. {
  288. return AZ::Failure(AZStd::string::format("Failed to read Gems for project: %s\n", m_currentProjectName.c_str()));
  289. }
  290. QObject::connect(&m_fileWatcher, &QFileSystemWatcher::directoryChanged, this, &GUIApplicationManager::DirectoryChanged);
  291. QObject::connect(&m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &GUIApplicationManager::FileChanged);
  292. return AZ::Success();
  293. }
  294. void GUIApplicationManager::DirectoryChanged(const QString& directory)
  295. {
  296. UpdateTab(directory.toUtf8().data());
  297. }
  298. void GUIApplicationManager::FileChanged(const QString& path)
  299. {
  300. // FileChanged will only be called when engine or gem seed files are updated
  301. // Otherwilse DirectoryChanged should be triggered
  302. AZStd::string extension;
  303. AzFramework::StringFunc::Path::GetExtension(path.toUtf8().data(), extension);
  304. extension = extension.starts_with(".") ? extension.substr(1) : extension;
  305. if (extension == AzToolsFramework::AssetSeedManager::GetSeedFileExtension())
  306. {
  307. UpdateTab(GetSeedListsFolder());
  308. }
  309. // Many applications save an open file by writing a new file and then deleting the old one
  310. // Add the file path back if it has been removed from the watcher file list
  311. if (!m_fileWatcher.files().contains(path))
  312. {
  313. m_fileWatcher.addPath(path);
  314. }
  315. }
  316. void GUIApplicationManager::ApplyConfig()
  317. {
  318. m_mainWindow->ApplyConfig();
  319. }
  320. } // namespace AssetBundler
  321. #include <source/utils/moc_GUIApplicationManager.cpp>