3
0

AtomToolsMainWindow.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  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 <Atom/RHI/Factory.h>
  9. #include <AtomToolsFramework/PerformanceMonitor/PerformanceMonitorRequestBus.h>
  10. #include <AtomToolsFramework/Util/Util.h>
  11. #include <AtomToolsFramework/Window/AtomToolsMainWindow.h>
  12. #include <AzCore/Name/Name.h>
  13. #include <AzCore/Utils/Utils.h>
  14. #include <AzCore/std/containers/map.h>
  15. #include <AzCore/std/sort.h>
  16. #include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
  17. #include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
  18. #include <QClipboard>
  19. #include <QCloseEvent>
  20. #include <QFileDialog>
  21. #include <QInputDialog>
  22. #include <QMenu>
  23. #include <QMenuBar>
  24. #include <QMessageBox>
  25. #include <QStatusBar>
  26. #include <QVBoxLayout>
  27. namespace AtomToolsFramework
  28. {
  29. AtomToolsMainWindow::AtomToolsMainWindow(const AZ::Crc32& toolId, const QString& objectName, QWidget* parent)
  30. : Base(parent)
  31. , m_toolId(toolId)
  32. , m_advancedDockManager(new AzQtComponents::FancyDocking(this, objectName.toUtf8().constData()))
  33. , m_mainWindowWrapper(new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionAutoTitleBarButtons))
  34. {
  35. setObjectName(objectName);
  36. setDockNestingEnabled(true);
  37. setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
  38. setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
  39. setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
  40. setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
  41. m_statusMessage = new QLabel(statusBar());
  42. statusBar()->addPermanentWidget(m_statusMessage, 1);
  43. auto centralWidget = new QWidget(this);
  44. auto centralWidgetLayout = new QVBoxLayout(centralWidget);
  45. centralWidgetLayout->setMargin(0);
  46. centralWidgetLayout->setContentsMargins(0, 0, 0, 0);
  47. centralWidget->setLayout(centralWidgetLayout);
  48. setCentralWidget(centralWidget);
  49. m_assetBrowser = new AtomToolsAssetBrowser(this);
  50. AddDockWidget("Asset Browser", m_assetBrowser, Qt::BottomDockWidgetArea);
  51. AddDockWidget("Python Terminal", new AzToolsFramework::CScriptTermDialog, Qt::BottomDockWidgetArea);
  52. SetDockWidgetVisible("Python Terminal", false);
  53. m_logPanel = new AzToolsFramework::LogPanel::TracePrintFLogPanel(this);
  54. m_logPanel->AddLogTab(AzToolsFramework::LogPanel::TabSettings("Log", "", ""));
  55. AddDockWidget("Logging", m_logPanel, Qt::BottomDockWidgetArea);
  56. SetDockWidgetVisible("Logging", false);
  57. SetupMetrics();
  58. UpdateWindowTitle();
  59. resize(1280, 1024);
  60. // Manage saving window geometry, restoring state window is shown for the first time
  61. m_mainWindowWrapper->setGuest(this);
  62. m_mainWindowWrapper->enableSaveRestoreGeometry(
  63. QApplication::organizationName(), QApplication::applicationName(), "mainWindowGeometry");
  64. AtomToolsMainWindowRequestBus::Handler::BusConnect(m_toolId);
  65. AtomToolsMainMenuRequestBus::Handler::BusConnect(m_toolId);
  66. QueueUpdateMenus(true);
  67. }
  68. AtomToolsMainWindow::~AtomToolsMainWindow()
  69. {
  70. PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, false);
  71. AtomToolsMainWindowRequestBus::Handler::BusDisconnect();
  72. AtomToolsMainMenuRequestBus::Handler::BusDisconnect();
  73. }
  74. void AtomToolsMainWindow::ActivateWindow()
  75. {
  76. show();
  77. raise();
  78. activateWindow();
  79. }
  80. bool AtomToolsMainWindow::AddDockWidget(const AZStd::string& name, QWidget* widget, uint32_t area)
  81. {
  82. auto dockWidget = qobject_cast<QDockWidget*>(widget);
  83. if (!dockWidget)
  84. {
  85. // If the widget being added is not already dockable then add a container dock widget for it
  86. dockWidget = new AzQtComponents::StyledDockWidget(name.c_str(), this);
  87. dockWidget->setWidget(widget);
  88. widget->setWindowTitle(name.c_str());
  89. widget->setObjectName(QString("%1_Widget").arg(name.c_str()));
  90. widget->setMinimumSize(QSize(300, 300));
  91. widget->setParent(dockWidget);
  92. widget->setVisible(true);
  93. }
  94. // Rename, resize, and reparent the dock widget for this main window
  95. dockWidget->setWindowTitle(name.c_str());
  96. dockWidget->setObjectName(QString("%1_DockWidget").arg(name.c_str()));
  97. dockWidget->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
  98. dockWidget->setMinimumSize(QSize(300, 300));
  99. dockWidget->setParent(this);
  100. dockWidget->setVisible(true);
  101. addDockWidget(aznumeric_cast<Qt::DockWidgetArea>(area), dockWidget);
  102. resizeDocks({ dockWidget }, { 400 }, Qt::Horizontal);
  103. resizeDocks({ dockWidget }, { 400 }, Qt::Vertical);
  104. QueueUpdateMenus(true);
  105. return true;
  106. }
  107. void AtomToolsMainWindow::RemoveDockWidget(const AZStd::string& name)
  108. {
  109. for (auto dockWidget : findChildren<QDockWidget*>())
  110. {
  111. if (dockWidget->windowTitle().compare(name.c_str(), Qt::CaseInsensitive) == 0)
  112. {
  113. delete dockWidget;
  114. QueueUpdateMenus(true);
  115. break;
  116. }
  117. }
  118. }
  119. void AtomToolsMainWindow::SetDockWidgetVisible(const AZStd::string& name, bool visible)
  120. {
  121. for (auto dockWidget : findChildren<QDockWidget*>())
  122. {
  123. if (dockWidget->windowTitle().compare(name.c_str(), Qt::CaseInsensitive) == 0)
  124. {
  125. if (auto tabWidget = AzQtComponents::DockTabWidget::ParentTabWidget(dockWidget))
  126. {
  127. // If the dock widget is tabbed, then set it as the active tab
  128. int index = tabWidget->indexOf(dockWidget);
  129. if (visible)
  130. {
  131. tabWidget->setCurrentIndex(index);
  132. }
  133. tabWidget->setTabVisible(index, visible);
  134. }
  135. else
  136. {
  137. // Otherwise just show the widget
  138. m_advancedDockManager->restoreDockWidget(dockWidget);
  139. }
  140. dockWidget->setVisible(visible);
  141. break;
  142. }
  143. }
  144. }
  145. bool AtomToolsMainWindow::IsDockWidgetVisible(const AZStd::string& name) const
  146. {
  147. for (auto dockWidget : findChildren<QDockWidget*>())
  148. {
  149. if (dockWidget->windowTitle().compare(name.c_str(), Qt::CaseInsensitive) == 0)
  150. {
  151. return dockWidget->isVisible();
  152. }
  153. }
  154. return false;
  155. }
  156. AZStd::vector<AZStd::string> AtomToolsMainWindow::GetDockWidgetNames() const
  157. {
  158. AZStd::vector<AZStd::string> names;
  159. names.reserve(children().size());
  160. for (auto dockWidget : findChildren<QDockWidget*>())
  161. {
  162. names.push_back(dockWidget->windowTitle().toUtf8().constData());
  163. }
  164. AZStd::sort(names.begin(), names.end());
  165. return names;
  166. }
  167. void AtomToolsMainWindow::SetStatusMessage(const AZStd::string& message)
  168. {
  169. m_statusMessage->setText(QString("<font color=\"White\">%1</font>").arg(message.c_str()));
  170. }
  171. void AtomToolsMainWindow::SetStatusWarning(const AZStd::string& message)
  172. {
  173. m_statusMessage->setText(QString("<font color=\"Yellow\">%1</font>").arg(message.c_str()));
  174. }
  175. void AtomToolsMainWindow::SetStatusError(const AZStd::string& message)
  176. {
  177. m_statusMessage->setText(QString("<font color=\"Red\">%1</font>").arg(message.c_str()));
  178. }
  179. void AtomToolsMainWindow::QueueUpdateMenus(bool rebuildMenus)
  180. {
  181. m_rebuildMenus = m_rebuildMenus || rebuildMenus;
  182. if (!m_updateMenus)
  183. {
  184. m_updateMenus = true;
  185. QTimer::singleShot(0, this, [this]() {
  186. if (m_rebuildMenus)
  187. {
  188. // Clearing all actions that were added directly to the menu bar
  189. menuBar()->clear();
  190. // Instead of destroying and recreating the menu bar, destroying the individual child menus to prevent the UI from
  191. // popping when the menu bar is recreated
  192. for (auto menu : menuBar()->findChildren<QMenu*>(QString(), Qt::FindDirectChildrenOnly))
  193. {
  194. delete menu;
  195. }
  196. AtomToolsMainMenuRequestBus::Event(m_toolId, &AtomToolsMainMenuRequestBus::Events::CreateMenus, menuBar());
  197. }
  198. AtomToolsMainMenuRequestBus::Event(m_toolId, &AtomToolsMainMenuRequestBus::Events::UpdateMenus, menuBar());
  199. m_updateMenus = false;
  200. m_rebuildMenus = false;
  201. });
  202. }
  203. }
  204. void AtomToolsMainWindow::CreateMenus(QMenuBar* menuBar)
  205. {
  206. m_menuFile = menuBar->addMenu("&File");
  207. m_menuFile->setObjectName("menuFile");
  208. m_menuEdit = menuBar->addMenu("&Edit");
  209. m_menuEdit->setObjectName("menuEdit");
  210. m_menuView = menuBar->addMenu("&View");
  211. m_menuView->setObjectName("menuView");
  212. m_menuTools = menuBar->addMenu("&Tools");
  213. m_menuTools->setObjectName("menuTools");
  214. m_menuHelp = menuBar->addMenu("&Help");
  215. m_menuHelp->setObjectName("menuHelp");
  216. BuildScriptsMenu();
  217. m_menuFile->addSeparator();
  218. m_menuFile->addAction(tr("E&xit"), [this]() {
  219. close();
  220. }, QKeySequence::Quit);
  221. BuildDockingMenu();
  222. m_menuTools->addSeparator();
  223. BuildLayoutsMenu();
  224. m_menuView->addSeparator();
  225. m_menuTools->addAction(tr("&Settings..."), [this]() {
  226. OpenSettingsDialog();
  227. }, QKeySequence::Preferences);
  228. m_menuHelp->addAction(tr("&Help..."), [this]() {
  229. OpenHelpDialog();
  230. }, QKeySequence::HelpContents);
  231. m_menuHelp->addAction(tr("&About..."), [this]() {
  232. OpenAboutDialog();
  233. });
  234. connect(m_menuEdit, &QMenu::aboutToShow, menuBar, [toolId = m_toolId](){
  235. AtomToolsMainWindowRequestBus::Event(toolId, &AtomToolsMainWindowRequestBus::Events::QueueUpdateMenus, false);
  236. });
  237. connect(QApplication::clipboard(), &QClipboard::dataChanged, menuBar, [toolId = m_toolId](){
  238. AtomToolsMainWindowRequestBus::Event(toolId, &AtomToolsMainWindowRequestBus::Events::QueueUpdateMenus, false);
  239. });
  240. }
  241. void AtomToolsMainWindow::UpdateMenus([[maybe_unused]] QMenuBar* menuBar)
  242. {
  243. }
  244. void AtomToolsMainWindow::PopulateSettingsInspector(InspectorWidget* inspector) const
  245. {
  246. m_applicationSettingsGroup = CreateSettingsPropertyGroup(
  247. "Application Settings",
  248. "Application Settings",
  249. { CreateSettingsPropertyValue(
  250. "/O3DE/AtomToolsFramework/Application/ClearLogOnStart",
  251. "Clear Log On Start",
  252. "Clear the application log on startup",
  253. false),
  254. CreateSettingsPropertyValue(
  255. "/O3DE/AtomToolsFramework/Application/EnableSourceControl",
  256. "Enable Source Control",
  257. "Enable source control for the application if it is available",
  258. false),
  259. CreateSettingsPropertyValue(
  260. "/O3DE/AtomToolsFramework/Application/IgnoreCacheFolder",
  261. "Ignore Files In Cache Folder",
  262. "This toggles whether or not files located in the cache folder appear in the asset browser, file selection dialogs, and "
  263. "during file enumeration. Changing this setting may require restarting the application to take effect in some areas.",
  264. true),
  265. CreateSettingsPropertyValue(
  266. "/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenActive",
  267. "Update Interval When Active",
  268. "Minimum delay between ticks (in milliseconds) when the application has focus",
  269. aznumeric_cast<AZ::s64>(1),
  270. aznumeric_cast<AZ::s64>(1),
  271. aznumeric_cast<AZ::s64>(1000)),
  272. CreateSettingsPropertyValue(
  273. "/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenNotActive",
  274. "Update Interval When Not Active",
  275. "Minimum delay between ticks (in milliseconds) when the application does not have focus",
  276. aznumeric_cast<AZ::s64>(250),
  277. aznumeric_cast<AZ::s64>(1),
  278. aznumeric_cast<AZ::s64>(1000)),
  279. CreateSettingsPropertyValue(
  280. "/O3DE/AtomToolsFramework/Application/AllowMultipleInstances",
  281. "Allow Multiple Instances",
  282. "Allow multiple instances of the application to run",
  283. false) });
  284. inspector->AddGroup(
  285. m_applicationSettingsGroup->m_name,
  286. m_applicationSettingsGroup->m_displayName,
  287. m_applicationSettingsGroup->m_description,
  288. new InspectorPropertyGroupWidget(
  289. m_applicationSettingsGroup.get(), m_applicationSettingsGroup.get(), azrtti_typeid<DynamicPropertyGroup>()));
  290. m_assetBrowserSettingsGroup = CreateSettingsPropertyGroup(
  291. "Asset Browser Settings",
  292. "Asset Browser Settings",
  293. { CreateSettingsPropertyValue(
  294. "/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFiles",
  295. "Prompt To Open Multiple Files",
  296. "Confirm before opening multiple files",
  297. true),
  298. CreateSettingsPropertyValue(
  299. "/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFilesThreshold",
  300. "Prompt To Open Multiple Files Threshold",
  301. "Maximum number of files that can be selected before prompting for confirmation",
  302. aznumeric_cast<AZ::s64>(10),
  303. aznumeric_cast<AZ::s64>(1),
  304. aznumeric_cast<AZ::s64>(100)) });
  305. inspector->AddGroup(
  306. m_assetBrowserSettingsGroup->m_name,
  307. m_assetBrowserSettingsGroup->m_displayName,
  308. m_assetBrowserSettingsGroup->m_description,
  309. new InspectorPropertyGroupWidget(
  310. m_assetBrowserSettingsGroup.get(), m_assetBrowserSettingsGroup.get(), azrtti_typeid<DynamicPropertyGroup>()));
  311. }
  312. void AtomToolsMainWindow::OpenSettingsDialog()
  313. {
  314. SettingsDialog dialog(this);
  315. dialog.GetInspector()->AddGroupsBegin();
  316. PopulateSettingsInspector(dialog.GetInspector());
  317. dialog.GetInspector()->AddGroupsEnd();
  318. // Temporarily forcing fixed size to prevent the dialog size from being overridden after being shown
  319. dialog.setFixedSize(800, 400);
  320. dialog.show();
  321. dialog.setMinimumSize(0, 0);
  322. dialog.setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
  323. dialog.exec();
  324. OnSettingsDialogClosed();
  325. }
  326. void AtomToolsMainWindow::OnSettingsDialogClosed()
  327. {
  328. }
  329. AZStd::string AtomToolsMainWindow::GetHelpDialogText() const
  330. {
  331. return AZStd::string();
  332. }
  333. void AtomToolsMainWindow::OpenHelpDialog()
  334. {
  335. QMessageBox::information(this, windowTitle(), GetHelpDialogText().c_str());
  336. }
  337. void AtomToolsMainWindow::OpenAboutDialog()
  338. {
  339. QMessageBox::about(this, windowTitle(), QApplication::applicationName());
  340. }
  341. void AtomToolsMainWindow::showEvent(QShowEvent* showEvent)
  342. {
  343. if (!m_shownBefore)
  344. {
  345. m_shownBefore = true;
  346. m_defaultWindowState = m_advancedDockManager->saveState();
  347. m_mainWindowWrapper->showFromSettings();
  348. RestoreSavedLayout();
  349. }
  350. Base::showEvent(showEvent);
  351. }
  352. void AtomToolsMainWindow::closeEvent(QCloseEvent* closeEvent)
  353. {
  354. if (closeEvent->isAccepted())
  355. {
  356. const QByteArray windowState = m_advancedDockManager->saveState();
  357. SetSettingsObject("/O3DE/AtomToolsFramework/MainWindow/WindowState", AZStd::string(windowState.begin(), windowState.end()));
  358. }
  359. Base::closeEvent(closeEvent);
  360. }
  361. void AtomToolsMainWindow::BuildDockingMenu()
  362. {
  363. auto dockWidgets = findChildren<QDockWidget*>();
  364. AZStd::sort(
  365. dockWidgets.begin(),
  366. dockWidgets.end(),
  367. [](QDockWidget* a, QDockWidget* b)
  368. {
  369. return a->windowTitle() < b->windowTitle();
  370. });
  371. for (auto dockWidget : dockWidgets)
  372. {
  373. const auto dockWidgetName = dockWidget->windowTitle();
  374. if (!dockWidgetName.isEmpty())
  375. {
  376. auto dockAction = m_menuTools->addAction(
  377. dockWidgetName,
  378. [this, dockWidgetName](const bool checked)
  379. {
  380. SetDockWidgetVisible(dockWidgetName.toUtf8().constData(), checked);
  381. });
  382. dockAction->setCheckable(true);
  383. dockAction->setChecked(dockWidget->isVisible());
  384. connect(dockWidget, &QDockWidget::visibilityChanged, dockAction, &QAction::setChecked);
  385. }
  386. }
  387. }
  388. void AtomToolsMainWindow::BuildLayoutsMenu()
  389. {
  390. QMenu* layoutSettingsMenu = m_menuView->addMenu(tr("Layouts"));
  391. connect(
  392. layoutSettingsMenu,
  393. &QMenu::aboutToShow,
  394. this,
  395. [this, layoutSettingsMenu]()
  396. {
  397. // Delete all previously registered menu actions before it is repopulated from settings.
  398. layoutSettingsMenu->clear();
  399. // Register actions for all non deletable, predefined, system layouts declared in the registry.
  400. for (const auto& layoutPair : GetSettingsObject(ToolLayoutSettingsKey, LayoutSettingsMap()))
  401. {
  402. const auto& layoutName = layoutPair.first;
  403. const auto& windowState = layoutPair.second;
  404. if (!layoutName.empty() && layoutName != "Default" && !windowState.empty())
  405. {
  406. layoutSettingsMenu->addAction(
  407. layoutName.c_str(),
  408. [this, windowState]()
  409. {
  410. m_advancedDockManager->restoreState(
  411. QByteArray(windowState.data(), aznumeric_cast<int>(windowState.size())));
  412. });
  413. }
  414. }
  415. layoutSettingsMenu->addSeparator();
  416. // Register actions for all of the layouts that were previously saved from within the application.
  417. for (const auto& layoutPair : GetSettingsObject(UserLayoutSettingsKey, LayoutSettingsMap()))
  418. {
  419. const auto& layoutName = layoutPair.first;
  420. const auto& windowState = layoutPair.second;
  421. if (!layoutName.empty() && layoutName != "Default" && !windowState.empty())
  422. {
  423. QMenu* layoutMenu = layoutSettingsMenu->addMenu(layoutName.c_str());
  424. // Since these layouts were created and saved by the user, give them the option to restore and delete them.
  425. layoutMenu->addAction(
  426. tr("Load"),
  427. [this, windowState]()
  428. {
  429. m_advancedDockManager->restoreState(
  430. QByteArray(windowState.data(), aznumeric_cast<int>(windowState.size())));
  431. });
  432. layoutMenu->addAction(
  433. tr("Delete"),
  434. [layoutName]()
  435. {
  436. auto userLayoutSettings = GetSettingsObject(UserLayoutSettingsKey, LayoutSettingsMap());
  437. userLayoutSettings.erase(layoutName);
  438. SetSettingsObject(UserLayoutSettingsKey, userLayoutSettings);
  439. });
  440. }
  441. }
  442. // Saving layouts prompts the user for a layout name then appends that layout to the existing settings which will be
  443. // saved on shut down. The layout name is reformatted as a display name, so that the casing is consistent for all
  444. // layouts.
  445. layoutSettingsMenu->addAction(
  446. tr("Save Layout..."),
  447. [this]()
  448. {
  449. const AZStd::string layoutName =
  450. GetDisplayNameFromText(QInputDialog::getText(this, tr("Layout Name"), QString()).toUtf8().constData());
  451. if (!layoutName.empty() && layoutName != "Default")
  452. {
  453. auto userLayoutSettings = GetSettingsObject(UserLayoutSettingsKey, LayoutSettingsMap());
  454. const QByteArray windowState = m_advancedDockManager->saveState();
  455. userLayoutSettings[layoutName] = AZStd::string(windowState.begin(), windowState.end());
  456. SetSettingsObject(UserLayoutSettingsKey, userLayoutSettings);
  457. }
  458. });
  459. layoutSettingsMenu->addSeparator();
  460. layoutSettingsMenu->addAction(
  461. tr("Restore Default Layout"),
  462. [this]()
  463. {
  464. RestoreDefaultLayout();
  465. });
  466. });
  467. }
  468. void AtomToolsMainWindow::BuildScriptsMenu()
  469. {
  470. QMenu* scriptsMenu = m_menuFile->addMenu(tr("Python Scripts"));
  471. connect(scriptsMenu, &QMenu::aboutToShow, this, [scriptsMenu]() {
  472. scriptsMenu->clear();
  473. AddRegisteredScriptToMenu(scriptsMenu, "/O3DE/AtomToolsFramework/MainWindow/FileMenuScripts", {});
  474. });
  475. }
  476. void AtomToolsMainWindow::RestoreDefaultLayout()
  477. {
  478. // Search all user and system layout settings for a data-driven default state before applying the hard-coded initial layout.
  479. // Settings are being used for a data-driven default state because it was simply easier to configure the layout in the running
  480. // application, save it, and restore it instead of attempting to achieve the desired layout through code.
  481. const auto& toolLayoutSettings = GetSettingsObject(ToolLayoutSettingsKey, LayoutSettingsMap());
  482. if (const auto it = toolLayoutSettings.find("Default"); it != toolLayoutSettings.end())
  483. {
  484. const auto& windowState = it->second;
  485. m_advancedDockManager->restoreState(QByteArray(windowState.data(), aznumeric_cast<int>(windowState.size())));
  486. return;
  487. }
  488. m_advancedDockManager->restoreState(m_defaultWindowState);
  489. }
  490. void AtomToolsMainWindow::RestoreSavedLayout()
  491. {
  492. // Attempt to restore the layout that was saved the last time the application was closed.
  493. const AZStd::string windowState = GetSettingsObject("/O3DE/AtomToolsFramework/MainWindow/WindowState", AZStd::string());
  494. if (!windowState.empty())
  495. {
  496. m_advancedDockManager->restoreState(QByteArray(windowState.data(), aznumeric_cast<int>(windowState.size())));
  497. return;
  498. }
  499. // If there are no settings for the last saved layout then attempt to restore the default layout from settings or the initial
  500. // hardcoded layout.
  501. RestoreDefaultLayout();
  502. }
  503. void AtomToolsMainWindow::SetupMetrics()
  504. {
  505. m_statusBarCpuTime = new QLabel(this);
  506. statusBar()->addPermanentWidget(m_statusBarCpuTime);
  507. m_statusBarGpuTime = new QLabel(this);
  508. statusBar()->addPermanentWidget(m_statusBarGpuTime);
  509. m_statusBarFps = new QLabel(this);
  510. statusBar()->addPermanentWidget(m_statusBarFps);
  511. static constexpr int UpdateIntervalMs = 1000;
  512. m_metricsTimer.setInterval(UpdateIntervalMs);
  513. m_metricsTimer.start();
  514. connect(&m_metricsTimer, &QTimer::timeout, this, &AtomToolsMainWindow::UpdateMetrics);
  515. PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, true);
  516. UpdateMetrics();
  517. }
  518. void AtomToolsMainWindow::UpdateMetrics()
  519. {
  520. PerformanceMetrics metrics = {};
  521. PerformanceMonitorRequestBus::BroadcastResult(metrics, &PerformanceMonitorRequestBus::Handler::GetMetrics);
  522. m_statusBarCpuTime->setText(tr("CPU Time %1 ms").arg(QString::number(metrics.m_cpuFrameTimeMs, 'f', 2)));
  523. m_statusBarGpuTime->setText(tr("GPU Time %1 ms").arg(QString::number(metrics.m_gpuFrameTimeMs, 'f', 2)));
  524. int frameRate = metrics.m_cpuFrameTimeMs > 0 ? aznumeric_cast<int>(1000 / metrics.m_cpuFrameTimeMs) : 0;
  525. m_statusBarFps->setText(tr("FPS %1").arg(QString::number(frameRate)));
  526. }
  527. void AtomToolsMainWindow::UpdateWindowTitle()
  528. {
  529. if (AZ::RHI::Factory::IsReady())
  530. {
  531. const AZ::Name apiName = AZ::RHI::Factory::Get().GetName();
  532. setWindowTitle(tr("%1 (%2)").arg(QApplication::applicationName()).arg(apiName.GetCStr()));
  533. AZ_Error("AtomToolsMainWindow", !apiName.IsEmpty(), "Render API name not found");
  534. return;
  535. }
  536. setWindowTitle(QApplication::applicationName());
  537. }
  538. } // namespace AtomToolsFramework