AtomToolsMainWindow.cpp 26 KB

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