MainWindow.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233
  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 "EditorDefs.h"
  9. #include "MainWindow.h"
  10. #include <algorithm>
  11. // Qt
  12. #ifdef Q_OS_WIN
  13. #include <QAbstractEventDispatcher>
  14. #endif
  15. #include <QDebug>
  16. #include <QHBoxLayout>
  17. #include <QInputDialog>
  18. #include <QMenuBar>
  19. #include <QMessageBox>
  20. #include <QToolBar>
  21. // AzCore
  22. #include <AzCore/Component/ComponentApplication.h>
  23. #include <AzCore/Interface/Interface.h>
  24. #include <AzCore/RTTI/BehaviorContext.h>
  25. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  26. #include <AzCore/std/smart_ptr/make_shared.h>
  27. #include <AzCore/Utils/Utils.h>
  28. // AzFramework
  29. #include <AzFramework/API/ApplicationAPI.h>
  30. #include <AzFramework/Asset/AssetSystemBus.h>
  31. #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
  32. #include <AzFramework/Network/SocketConnection.h>
  33. #include <AzFramework/Asset/AssetSystemComponent.h>
  34. #include <AzFramework/Viewport/CameraInput.h>
  35. // AzToolsFramework
  36. #include <AzToolsFramework/API/EditorCameraBus.h>
  37. #include <AzToolsFramework/Application/Ticker.h>
  38. #include <AzToolsFramework/API/EditorWindowRequestBus.h>
  39. #include <AzToolsFramework/API/EditorAnimationSystemRequestBus.h>
  40. #include <AzToolsFramework/Editor/ActionManagerUtils.h>
  41. #include <AzToolsFramework/PaintBrush/GlobalPaintBrushSettingsWindow.h>
  42. #include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
  43. #include <AzToolsFramework/SourceControl/QtSourceControlNotificationHandler.h>
  44. #include <AzToolsFramework/Viewport/LocalViewBookmarkLoader.h>
  45. #include <AzToolsFramework/Viewport/ViewportSettings.h>
  46. #include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
  47. // AzQtComponents
  48. #include <AzQtComponents/Buses/ShortcutDispatch.h>
  49. #include <AzQtComponents/Components/DockMainWindow.h>
  50. #include <AzQtComponents/Components/InputDialog.h>
  51. #include <AzQtComponents/Components/Style.h>
  52. #include <AzQtComponents/Components/Widgets/SpinBox.h>
  53. #include <AzQtComponents/Components/WindowDecorationWrapper.h>
  54. #include <AzQtComponents/DragAndDrop/MainWindowDragAndDrop.h>
  55. // Editor
  56. #include "Resource.h"
  57. #include "LayoutWnd.h"
  58. #include "AssetImporter/AssetImporterManager/AssetImporterManager.h"
  59. #include "AssetImporter/AssetImporterManager/AssetImporterDragAndDropHandler.h"
  60. #include "CryEdit.h"
  61. #include "Controls/ConsoleSCB.h"
  62. #include "ViewManager.h"
  63. #include "CryEditDoc.h"
  64. #include "ToolBox.h"
  65. #include "LevelIndependentFileMan.h"
  66. #include "GameEngine.h"
  67. #include "MainStatusBar.h"
  68. #include "Core/QtEditorApplication.h"
  69. #include "UndoDropDown.h"
  70. #include "EditorViewportSettings.h"
  71. #include "QtViewPaneManager.h"
  72. #include "ViewPane.h"
  73. #include "Include/Command.h"
  74. #include "Commands/CommandManager.h"
  75. #include "SettingsManagerDialog.h"
  76. #include "TrackView/TrackViewDialog.h"
  77. #include "ErrorReportDialog.h"
  78. #include "Dialogs/PythonScriptsDialog.h"
  79. #include "AzAssetBrowser/AzAssetBrowserWindow.h"
  80. #include "AssetEditor/AssetEditorWindow.h"
  81. #include <ImGuiBus.h>
  82. #include <AzToolsFramework/Viewport/ViewportMessages.h>
  83. #include <LmbrCentral/Audio/AudioSystemComponentBus.h>
  84. #include <Editor/EditorViewportCamera.h>
  85. using namespace AZ;
  86. using namespace AzQtComponents;
  87. using namespace AzToolsFramework;
  88. #define LAYOUTS_PATH "Editor\\Layouts\\"
  89. #define LAYOUTS_EXTENSION ".layout"
  90. #define LAYOUTS_WILDCARD "*.layout"
  91. #define DUMMY_LAYOUT_NAME "Dummy_Layout"
  92. class CEditorOpenViewCommand
  93. : public _i_reference_target_t
  94. {
  95. QString m_className;
  96. public:
  97. CEditorOpenViewCommand(IEditor* pEditor, const QString& className)
  98. : m_pEditor(pEditor)
  99. , m_className(className)
  100. {
  101. assert(m_pEditor);
  102. }
  103. void Execute()
  104. {
  105. // Create browse mode for this category.
  106. m_pEditor->OpenView(m_className);
  107. }
  108. private:
  109. IEditor* m_pEditor;
  110. };
  111. namespace
  112. {
  113. // The purpose of this vector is just holding shared pointers, so CEditorOpenViewCommand dtors are called at exit
  114. std::vector<_smart_ptr<CEditorOpenViewCommand> > s_openViewCmds;
  115. }
  116. class EngineConnectionListener
  117. : public AzFramework::EngineConnectionEvents::Bus::Handler
  118. , public AzFramework::AssetSystemInfoBus::Handler
  119. {
  120. public:
  121. using EConnectionState = AzFramework::SocketConnection::EConnectionState;
  122. public:
  123. EngineConnectionListener()
  124. : m_state(EConnectionState::Disconnected)
  125. {
  126. AzFramework::EngineConnectionEvents::Bus::Handler::BusConnect();
  127. AzFramework::AssetSystemInfoBus::Handler::BusConnect();
  128. AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
  129. if (engineConnection)
  130. {
  131. m_state = engineConnection->GetConnectionState();
  132. }
  133. }
  134. ~EngineConnectionListener() override
  135. {
  136. AzFramework::AssetSystemInfoBus::Handler::BusDisconnect();
  137. AzFramework::EngineConnectionEvents::Bus::Handler::BusDisconnect();
  138. }
  139. public:
  140. void Connected([[maybe_unused]] AzFramework::SocketConnection* connection) override
  141. {
  142. m_state = EConnectionState::Connected;
  143. }
  144. void Connecting([[maybe_unused]] AzFramework::SocketConnection* connection) override
  145. {
  146. m_state = EConnectionState::Connecting;
  147. }
  148. void Listening([[maybe_unused]] AzFramework::SocketConnection* connection) override
  149. {
  150. m_state = EConnectionState::Listening;
  151. }
  152. void Disconnecting([[maybe_unused]] AzFramework::SocketConnection* connection) override
  153. {
  154. m_state = EConnectionState::Disconnecting;
  155. }
  156. void Disconnected([[maybe_unused]] AzFramework::SocketConnection* connection) override
  157. {
  158. m_state = EConnectionState::Disconnected;
  159. }
  160. void AssetCompilationSuccess(const AZStd::string& assetPath) override
  161. {
  162. m_lastAssetProcessorTask = assetPath;
  163. }
  164. void AssetCompilationFailed(const AZStd::string& assetPath) override
  165. {
  166. m_failedJobs.insert(assetPath);
  167. }
  168. void CountOfAssetsInQueue(const int& count) override
  169. {
  170. m_pendingJobsCount = count;
  171. }
  172. int GetJobsCount() const
  173. {
  174. return m_pendingJobsCount;
  175. }
  176. AZStd::set<AZStd::string> FailedJobsList() const
  177. {
  178. return m_failedJobs;
  179. }
  180. AZStd::string LastAssetProcessorTask() const
  181. {
  182. return m_lastAssetProcessorTask;
  183. }
  184. public:
  185. EConnectionState GetState() const
  186. {
  187. return m_state;
  188. }
  189. private:
  190. EConnectionState m_state;
  191. int m_pendingJobsCount = 0;
  192. AZStd::set<AZStd::string> m_failedJobs;
  193. AZStd::string m_lastAssetProcessorTask;
  194. };
  195. namespace
  196. {
  197. void PyOpenViewPane(const char* viewClassName)
  198. {
  199. QtViewPaneManager::instance()->OpenPane(viewClassName);
  200. }
  201. void PyCloseViewPane(const char* viewClassName)
  202. {
  203. QtViewPaneManager::instance()->ClosePane(viewClassName);
  204. }
  205. bool PyIsViewPaneVisible(const char* viewClassName)
  206. {
  207. return QtViewPaneManager::instance()->IsVisible(viewClassName);
  208. }
  209. AZStd::vector<AZStd::string> PyGetViewPaneNames()
  210. {
  211. const QtViewPanes panes = QtViewPaneManager::instance()->GetRegisteredPanes();
  212. AZStd::vector<AZStd::string> names;
  213. names.reserve(panes.size());
  214. AZStd::transform(panes.begin(), panes.end(), AZStd::back_inserter(names), [](const QtViewPane& pane)
  215. {
  216. return AZStd::string(pane.m_name.toUtf8().constData());
  217. });
  218. return names;
  219. }
  220. void PyExit()
  221. {
  222. // Adding a single-shot QTimer to PyExit delays the QApplication::closeAllWindows call until
  223. // all the events in the event queue have been processed. Calling QApplication::closeAllWindows instead
  224. // of MainWindow::close ensures the Metal render window is cleaned up on macOS.
  225. QTimer::singleShot(0, qApp, &QApplication::closeAllWindows);
  226. }
  227. void PyExitNoPrompt()
  228. {
  229. // Set the level to "unmodified" so that it doesn't prompt to save on exit.
  230. GetIEditor()->GetDocument()->SetModifiedFlag(false);
  231. PyExit();
  232. }
  233. void PyTestOutput(const AZStd::string& output)
  234. {
  235. CCryEditApp::instance()->PrintAlways(output);
  236. }
  237. }
  238. /////////////////////////////////////////////////////////////////////////////
  239. // MainWindow
  240. /////////////////////////////////////////////////////////////////////////////
  241. MainWindow* MainWindow::m_instance = nullptr;
  242. MainWindow::MainWindow(QWidget* parent)
  243. : QMainWindow(parent)
  244. , m_oldMainFrame(nullptr)
  245. , m_viewPaneManager(QtViewPaneManager::instance())
  246. , m_undoStateAdapter(new UndoStackStateAdapter(this))
  247. , m_activeView(nullptr)
  248. , m_settings("O3DE", "O3DE")
  249. , m_assetImporterManager(new AssetImporterManager(this))
  250. , m_sourceControlNotifHandler(new AzToolsFramework::QtSourceControlNotificationHandler(this))
  251. , m_viewPaneHost(nullptr)
  252. , m_autoSaveTimer(nullptr)
  253. , m_autoRemindTimer(nullptr)
  254. , m_backgroundUpdateTimer(nullptr)
  255. , m_connectionLostTimer(new QTimer(this))
  256. {
  257. setObjectName("MainWindow"); // For IEditor::GetEditorMainWindow to work in plugins, where we can't link against MainWindow::instance()
  258. m_instance = this;
  259. //for new docking, create a DockMainWindow to host dock widgets so we can call QMainWindow::restoreState to restore docks without affecting our main toolbars.
  260. m_viewPaneHost = new AzQtComponents::DockMainWindow();
  261. m_viewPaneHost->setDockOptions(QMainWindow::GroupedDragging | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
  262. m_connectionListener = AZStd::make_shared<EngineConnectionListener>();
  263. QObject::connect(m_connectionLostTimer, &QTimer::timeout, this, &MainWindow::ShowConnectionDisconnectedDialog);
  264. setStatusBar(new MainStatusBar(this));
  265. setAttribute(Qt::WA_DeleteOnClose, true);
  266. GetIEditor()->RegisterNotifyListener(this);
  267. AssetImporterDragAndDropHandler* assetImporterDragAndDropHandler = new AssetImporterDragAndDropHandler(this, m_assetImporterManager);
  268. connect(assetImporterDragAndDropHandler, &AssetImporterDragAndDropHandler::OpenAssetImporterManager, this, &MainWindow::OnOpenAssetImporterManager);
  269. connect(assetImporterDragAndDropHandler, &AssetImporterDragAndDropHandler::OpenAssetImporterManagerWithSuggestedPath, this, &MainWindow::OnOpenAssetImporterManagerAtPath);
  270. setFocusPolicy(Qt::StrongFocus);
  271. setAcceptDrops(true);
  272. // special handling for escape key (outside ActionManager)
  273. auto* escapeAction = new QAction(this);
  274. escapeAction->setShortcut(QKeySequence(Qt::Key_Escape));
  275. addAction(escapeAction);
  276. connect(escapeAction, &QAction::triggered, this, &MainWindow::OnEscapeAction);
  277. const QSize minSize(800, 600);
  278. if (size().height() < minSize.height() || size().width() < minSize.width())
  279. {
  280. resize(size().expandedTo(minSize));
  281. }
  282. }
  283. void MainWindow::SystemTick()
  284. {
  285. AZ::ComponentApplication* componentApplication = nullptr;
  286. AZ::ComponentApplicationBus::BroadcastResult(componentApplication, &AZ::ComponentApplicationBus::Events::GetApplication);
  287. if (componentApplication)
  288. {
  289. componentApplication->TickSystem();
  290. }
  291. }
  292. #ifdef Q_OS_WIN
  293. HWND MainWindow::GetNativeHandle()
  294. {
  295. // if the parent widget is set, it's a window decoration wrapper
  296. // we use that instead, to ensure we're in lock step the code in CryEdit.cpp when it calls
  297. // InitGameSystem
  298. if (parentWidget() != nullptr)
  299. {
  300. assert(qobject_cast<AzQtComponents::WindowDecorationWrapper*>(parentWidget()));
  301. return QtUtil::getNativeHandle(parentWidget());
  302. }
  303. return QtUtil::getNativeHandle(this);
  304. }
  305. #endif // #ifdef Q_OS_WIN
  306. void MainWindow::OnOpenAssetImporterManager(const QStringList& dragAndDropFileList)
  307. {
  308. m_assetImporterManager->Exec(dragAndDropFileList);
  309. }
  310. void MainWindow::OnOpenAssetImporterManagerAtPath(const QStringList& dragAndDropFileList, const QString& path)
  311. {
  312. m_assetImporterManager->Exec(dragAndDropFileList, path);
  313. }
  314. CLayoutWnd* MainWindow::GetLayout() const
  315. {
  316. return m_pLayoutWnd;
  317. }
  318. CLayoutViewPane* MainWindow::GetActiveView() const
  319. {
  320. return m_activeView;
  321. }
  322. QtViewport* MainWindow::GetActiveViewport() const
  323. {
  324. return m_activeView ? qobject_cast<QtViewport*>(m_activeView->GetViewport()) : nullptr;
  325. }
  326. void MainWindow::SetActiveView(CLayoutViewPane* v)
  327. {
  328. m_activeView = v;
  329. }
  330. MainWindow::~MainWindow()
  331. {
  332. AzToolsFramework::SourceControlNotificationBus::Handler::BusDisconnect();
  333. m_connectionListener.reset();
  334. GetIEditor()->UnregisterNotifyListener(this);
  335. m_instance = nullptr;
  336. }
  337. void MainWindow::InitCentralWidget()
  338. {
  339. m_pLayoutWnd = new CLayoutWnd(&m_settings);
  340. // Set the central widgets before calling CreateLayout to avoid reparenting everything later
  341. setCentralWidget(m_viewPaneHost);
  342. m_viewPaneHost->setCentralWidget(m_pLayoutWnd);
  343. if (MainWindow::instance()->IsPreview())
  344. {
  345. m_pLayoutWnd->CreateLayout(ET_Layout0, true, ET_ViewportModel);
  346. }
  347. else
  348. {
  349. if (!m_pLayoutWnd->LoadConfig())
  350. {
  351. m_pLayoutWnd->CreateLayout(ET_Layout0);
  352. }
  353. }
  354. // make sure the layout wnd knows to reset it's layout and settings
  355. connect(m_viewPaneManager, &QtViewPaneManager::layoutReset, m_pLayoutWnd, &CLayoutWnd::ResetLayout);
  356. AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::Bus::Events::NotifyCentralWidgetInitialized);
  357. }
  358. void MainWindow::Initialize()
  359. {
  360. m_viewPaneManager->SetMainWindow(m_viewPaneHost, &m_settings, /*unused*/ QByteArray());
  361. RegisterStdViewClasses();
  362. InitCentralWidget();
  363. // load toolbars ("shelves") and macros
  364. GetIEditor()->GetToolBoxManager()->Load();
  365. m_editorActionsHandler.Initialize(this);
  366. InitStatusBar();
  367. AzToolsFramework::SourceControlNotificationBus::Handler::BusConnect();
  368. m_sourceControlNotifHandler->Init();
  369. if (!IsPreview())
  370. {
  371. RegisterOpenWndCommands();
  372. }
  373. ResetBackgroundUpdateTimer();
  374. ICVar* pBackgroundUpdatePeriod = gEnv->pConsole->GetCVar("ed_backgroundUpdatePeriod");
  375. if (pBackgroundUpdatePeriod)
  376. {
  377. pBackgroundUpdatePeriod->SetOnChangeCallback([](ICVar*) {
  378. MainWindow::instance()->ResetBackgroundUpdateTimer();
  379. });
  380. }
  381. AzToolsFramework::EditorEventsBus::Broadcast(&AzToolsFramework::EditorEvents::NotifyMainWindowInitialized, this);
  382. }
  383. void MainWindow::InitStatusBar()
  384. {
  385. StatusBar()->Init();
  386. connect(qobject_cast<StatusBarItem*>(StatusBar()->GetItem("connection")), &StatusBarItem::clicked, this, &MainWindow::OnConnectionStatusClicked);
  387. connect(StatusBar(), &MainStatusBar::requestStatusUpdate, this, &MainWindow::OnUpdateConnectionStatus);
  388. }
  389. CMainFrame* MainWindow::GetOldMainFrame() const
  390. {
  391. return m_oldMainFrame;
  392. }
  393. MainWindow* MainWindow::instance()
  394. {
  395. return m_instance;
  396. }
  397. void MainWindow::closeEvent(QCloseEvent* event)
  398. {
  399. gSettings.Save(true);
  400. AzFramework::SystemCursorState currentCursorState;
  401. bool isInGameMode = false;
  402. if (GetIEditor()->IsInGameMode())
  403. {
  404. isInGameMode = true;
  405. // Storecurrent state in case we need to restore Game Mode.
  406. AzFramework::InputSystemCursorRequestBus::EventResult(currentCursorState, AzFramework::InputDeviceMouse::Id,
  407. &AzFramework::InputSystemCursorRequests::GetSystemCursorState);
  408. // make sure the mouse is turned on before popping up any dialog boxes.
  409. AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
  410. &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
  411. AzFramework::SystemCursorState::UnconstrainedAndVisible);
  412. }
  413. if (GetIEditor()->GetDocument() && !GetIEditor()->GetDocument()->CanCloseFrame())
  414. {
  415. if (isInGameMode)
  416. {
  417. // make sure the mouse is turned back off if returning to the game.
  418. AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
  419. &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
  420. currentCursorState);
  421. }
  422. event->ignore();
  423. return;
  424. }
  425. SaveConfig();
  426. // Some of the panes may ask for confirmation to save changes before closing.
  427. if (!QtViewPaneManager::instance()->ClosePanesWithRollback(QVector<QString>()) ||
  428. !GetIEditor() ||
  429. !GetIEditor()->GetLevelIndependentFileMan()->PromptChangedFiles())
  430. {
  431. if (isInGameMode)
  432. {
  433. // make sure the mouse is turned back off if returning to the game.
  434. AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
  435. &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
  436. currentCursorState);
  437. }
  438. event->ignore();
  439. return;
  440. }
  441. Editor::EditorQtApplication::instance()->EnableOnIdle(false);
  442. if (GetIEditor()->GetDocument())
  443. {
  444. GetIEditor()->GetDocument()->SetModifiedFlag(false);
  445. GetIEditor()->GetDocument()->SetModifiedModules(eModifiedNothing);
  446. }
  447. // force clean up of all deferred deletes, so that we don't have any issues with windows from plugins not being deleted yet
  448. qApp->sendPostedEvents(nullptr, QEvent::DeferredDelete);
  449. QMainWindow::closeEvent(event);
  450. }
  451. void MainWindow::SaveConfig()
  452. {
  453. m_settings.setValue("mainWindowState", saveState());
  454. QtViewPaneManager::instance()->SaveLayout();
  455. if (m_pLayoutWnd)
  456. {
  457. m_pLayoutWnd->SaveConfig();
  458. }
  459. }
  460. void MainWindow::OnEscapeAction()
  461. {
  462. if (!CCryEditApp::instance()->IsInAutotestMode())
  463. {
  464. if (GetIEditor()->IsInGameMode())
  465. {
  466. GetIEditor()->SetInGameMode(false);
  467. }
  468. else
  469. {
  470. AzToolsFramework::EditorEvents::Bus::Broadcast(
  471. &AzToolsFramework::EditorEvents::OnEscape);
  472. }
  473. }
  474. }
  475. QWidget* MainWindow::CreateSpacerRightWidget()
  476. {
  477. QWidget* spacer = new QWidget(this);
  478. spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
  479. spacer->setVisible(true);
  480. return spacer;
  481. }
  482. UndoRedoToolButton::UndoRedoToolButton(QWidget* parent)
  483. : QToolButton(parent)
  484. {
  485. }
  486. void UndoRedoToolButton::Update(int count)
  487. {
  488. setEnabled(count > 0);
  489. }
  490. bool MainWindow::IsPreview() const
  491. {
  492. return GetIEditor()->IsInPreviewMode();
  493. }
  494. MainStatusBar* MainWindow::StatusBar() const
  495. {
  496. assert(statusBar()->inherits("MainStatusBar"));
  497. return static_cast<MainStatusBar*>(statusBar());
  498. }
  499. void MainWindow::OpenViewPane(int paneId)
  500. {
  501. OpenViewPane(QtViewPaneManager::instance()->GetPane(paneId));
  502. }
  503. void MainWindow::OpenViewPane(QtViewPane* pane)
  504. {
  505. if (pane && pane->IsValid())
  506. {
  507. QtViewPaneManager::instance()->OpenPane(pane->m_name);
  508. }
  509. else
  510. {
  511. if (pane)
  512. {
  513. qWarning() << Q_FUNC_INFO << "Invalid pane" << pane->m_id << pane->m_category << pane->m_name;
  514. }
  515. else
  516. {
  517. qWarning() << Q_FUNC_INFO << "Invalid pane";
  518. }
  519. }
  520. }
  521. void MainWindow::AdjustToolBarIconSize(AzQtComponents::ToolBar::ToolBarIconSize size)
  522. {
  523. const QList<QToolBar*> toolbars = findChildren<QToolBar*>();
  524. // make sure to set this back, so that the general settings page matches up with what the size is too
  525. if (gSettings.gui.nToolbarIconSize != static_cast<int>(size))
  526. {
  527. gSettings.gui.nToolbarIconSize = static_cast<int>(size);
  528. }
  529. for (auto toolbar : toolbars)
  530. {
  531. AzQtComponents::ToolBar::setToolBarIconSize(toolbar, size);
  532. }
  533. }
  534. void MainWindow::OnEditorNotifyEvent(EEditorNotifyEvent ev)
  535. {
  536. switch (ev)
  537. {
  538. case eNotify_OnEndSceneOpen:
  539. case eNotify_OnEndSceneSave:
  540. {
  541. auto cryEdit = CCryEditApp::instance();
  542. if (cryEdit)
  543. {
  544. cryEdit->SetEditorWindowTitle(nullptr, AZ::Utils::GetProjectDisplayName().c_str(), GetIEditor()->GetGameEngine()->GetLevelName());
  545. }
  546. }
  547. break;
  548. case eNotify_OnCloseScene:
  549. {
  550. auto cryEdit = CCryEditApp::instance();
  551. if (cryEdit)
  552. {
  553. cryEdit->SetEditorWindowTitle(nullptr, AZ::Utils::GetProjectDisplayName().c_str(), nullptr);
  554. }
  555. }
  556. break;
  557. case eNotify_OnRefCoordSysChange:
  558. emit UpdateRefCoordSys();
  559. break;
  560. case eNotify_OnInvalidateControls:
  561. InvalidateControls();
  562. break;
  563. }
  564. switch (ev)
  565. {
  566. case eNotify_OnBeginSceneOpen:
  567. case eNotify_OnBeginNewScene:
  568. case eNotify_OnCloseScene:
  569. StopAutoSaveTimers();
  570. break;
  571. case eNotify_OnEndSceneOpen:
  572. case eNotify_OnEndNewScene:
  573. StartAutoSaveTimers();
  574. break;
  575. }
  576. }
  577. void MainWindow::InvalidateControls()
  578. {
  579. emit UpdateRefCoordSys();
  580. }
  581. void MainWindow::RegisterStdViewClasses()
  582. {
  583. AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(this);
  584. CTrackViewDialog::RegisterViewClass();
  585. CErrorReportDialog::RegisterViewClass();
  586. CPythonScriptsDialog::RegisterViewClass();
  587. AzToolsFramework::CScriptTermDialog::RegisterViewClass();
  588. CConsoleSCB::RegisterViewClass();
  589. ConsoleVariableEditor::RegisterViewClass();
  590. CSettingsManagerDialog::RegisterViewClass();
  591. AzAssetBrowserWindow::RegisterViewClass();
  592. AssetEditorWindow::RegisterViewClass();
  593. AzToolsFramework::RegisterPaintBrushSettingsWindow();
  594. // Notify that views can now be registered
  595. AzToolsFramework::EditorEvents::Bus::Broadcast(
  596. &AzToolsFramework::EditorEvents::Bus::Events::NotifyRegisterViews);
  597. }
  598. void MainWindow::OnCustomizeToolbar()
  599. {
  600. /* TODO_KDAB, rest of CMainFrm::OnCustomize() goes here*/
  601. SaveConfig();
  602. }
  603. void MainWindow::RefreshStyle()
  604. {
  605. GetIEditor()->Notify(eNotify_OnStyleChanged);
  606. }
  607. void MainWindow::StopAutoSaveTimers()
  608. {
  609. if (m_autoSaveTimer)
  610. {
  611. delete m_autoSaveTimer;
  612. }
  613. if (m_autoRemindTimer)
  614. {
  615. delete m_autoRemindTimer;
  616. }
  617. m_autoSaveTimer = nullptr;
  618. m_autoRemindTimer = nullptr;
  619. }
  620. void MainWindow::StartAutoSaveTimers()
  621. {
  622. if (gSettings.autoBackupTime > 0 && gSettings.autoBackupEnabled)
  623. {
  624. m_autoSaveTimer = new QTimer(this);
  625. m_autoSaveTimer->start(gSettings.autoBackupTime * 1000 * 60);
  626. connect(
  627. m_autoSaveTimer,
  628. &QTimer::timeout,
  629. this,
  630. [&]()
  631. {
  632. if (gSettings.autoBackupEnabled)
  633. {
  634. // Call autosave function of CryEditApp
  635. GetIEditor()->GetDocument()->SaveAutoBackup();
  636. }
  637. });
  638. }
  639. if (gSettings.autoRemindTime > 0)
  640. {
  641. m_autoRemindTimer = new QTimer(this);
  642. m_autoRemindTimer->start(gSettings.autoRemindTime * 1000 * 60);
  643. connect(
  644. m_autoRemindTimer,
  645. &QTimer::timeout,
  646. this,
  647. [&]()
  648. {
  649. if (gSettings.autoRemindTime > 0)
  650. {
  651. // Remind to save.
  652. CCryEditApp::instance()->SaveAutoRemind();
  653. }
  654. });
  655. }
  656. }
  657. void MainWindow::ResetAutoSaveTimers()
  658. {
  659. StopAutoSaveTimers();
  660. StartAutoSaveTimers();
  661. }
  662. void MainWindow::ResetBackgroundUpdateTimer()
  663. {
  664. if (m_backgroundUpdateTimer)
  665. {
  666. delete m_backgroundUpdateTimer;
  667. m_backgroundUpdateTimer = nullptr;
  668. }
  669. if (gSettings.backgroundUpdatePeriod > 0)
  670. {
  671. m_backgroundUpdateTimer = new QTimer(this);
  672. m_backgroundUpdateTimer->start(gSettings.backgroundUpdatePeriod);
  673. connect(m_backgroundUpdateTimer, &QTimer::timeout, this, [&]() {
  674. // Make sure that visible editor window get low-fps updates while in the background
  675. CCryEditApp* pApp = CCryEditApp::instance();
  676. if (!isMinimized() && !pApp->IsWindowInForeground())
  677. {
  678. pApp->IdleProcessing(true);
  679. }
  680. });
  681. }
  682. }
  683. void MainWindow::OnStopAllSounds()
  684. {
  685. LmbrCentral::AudioSystemComponentRequestBus::Broadcast(&LmbrCentral::AudioSystemComponentRequestBus::Events::GlobalStopAllSounds);
  686. }
  687. void MainWindow::OnRefreshAudioSystem()
  688. {
  689. AZStd::string levelName;
  690. AzToolsFramework::EditorRequestBus::BroadcastResult(levelName, &AzToolsFramework::EditorRequestBus::Events::GetLevelName);
  691. AZStd::to_lower(levelName.begin(), levelName.end());
  692. if (levelName == "untitled")
  693. {
  694. levelName.clear();
  695. }
  696. LmbrCentral::AudioSystemComponentRequestBus::Broadcast(
  697. &LmbrCentral::AudioSystemComponentRequestBus::Events::GlobalRefreshAudio, AZStd::string_view{ levelName });
  698. }
  699. void MainWindow::SaveLayout()
  700. {
  701. const int MAX_LAYOUTS = ID_VIEW_LAYOUT_LAST - ID_VIEW_LAYOUT_FIRST + 1;
  702. if (m_viewPaneManager->LayoutNames(true).count() >= MAX_LAYOUTS)
  703. {
  704. QMessageBox::critical(this, tr("Maximum number of layouts reached"), tr("Please delete a saved layout before creating another."));
  705. return;
  706. }
  707. QString layoutName = InputDialog::getText(this, tr("Layout Name"), QString(), QLineEdit::Normal, QString(), "[a-z]+[a-z0-9\\-\\_]*");
  708. if (layoutName.isEmpty())
  709. {
  710. return;
  711. }
  712. if (m_viewPaneManager->HasLayout(layoutName))
  713. {
  714. QMessageBox box(this); // Not static so we can remove help button
  715. box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
  716. box.setText(tr("Overwrite Layout?"));
  717. box.setIcon(QMessageBox::Warning);
  718. box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
  719. box.setInformativeText(tr("The chosen layout name already exists. Do you want to overwrite it?"));
  720. if (box.exec() != QMessageBox::Yes)
  721. {
  722. SaveLayout();
  723. return;
  724. }
  725. }
  726. m_viewPaneManager->SaveLayout(layoutName);
  727. }
  728. void MainWindow::ViewDeletePaneLayout(const QString& layoutName)
  729. {
  730. if (layoutName.isEmpty())
  731. {
  732. return;
  733. }
  734. QMessageBox box(this); // Not static so we can remove help button
  735. box.setText(tr("Delete Layout?"));
  736. box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
  737. box.setIcon(QMessageBox::Warning);
  738. box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
  739. box.setInformativeText(tr("Are you sure you want to delete the layout '%1'?").arg(layoutName));
  740. if (box.exec() == QMessageBox::Yes)
  741. {
  742. m_viewPaneManager->RemoveLayout(layoutName);
  743. }
  744. }
  745. void MainWindow::ViewRenamePaneLayout(const QString& layoutName)
  746. {
  747. if (layoutName.isEmpty())
  748. {
  749. return;
  750. }
  751. QString newLayoutName;
  752. bool validName = false;
  753. while (!validName)
  754. {
  755. newLayoutName = InputDialog::getText(this, tr("Layout Name"), QString(), QLineEdit::Normal, QString(), "[a-z]+[a-z0-9\\-\\_]*");
  756. if (m_viewPaneManager->HasLayout(newLayoutName))
  757. {
  758. QMessageBox box(this); // Not static so we can remove help button
  759. box.setText(tr("Layout name already exists"));
  760. box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
  761. box.setIcon(QMessageBox::Warning);
  762. box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
  763. box.setInformativeText(tr("The layout name '%1' already exists, please choose a different name").arg(newLayoutName));
  764. if (box.exec() == QMessageBox::No)
  765. {
  766. return;
  767. }
  768. }
  769. else
  770. {
  771. validName = true;
  772. }
  773. }
  774. m_viewPaneManager->RenameLayout(layoutName, newLayoutName);
  775. }
  776. void MainWindow::ViewLoadPaneLayout(const QString& layoutName)
  777. {
  778. if (!layoutName.isEmpty())
  779. {
  780. m_viewPaneManager->RestoreLayout(layoutName);
  781. }
  782. }
  783. void MainWindow::ViewSavePaneLayout(const QString& layoutName)
  784. {
  785. if (layoutName.isEmpty())
  786. {
  787. return;
  788. }
  789. QMessageBox box(this); // Not static so we can remove help button
  790. box.setText(tr("Overwrite Layout?"));
  791. box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
  792. box.setIcon(QMessageBox::Warning);
  793. box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
  794. box.setInformativeText(tr("Do you want to overwrite the layout '%1' with the current one?").arg(layoutName));
  795. if (box.exec() == QMessageBox::Yes)
  796. {
  797. m_viewPaneManager->SaveLayout(layoutName);
  798. }
  799. }
  800. void MainWindow::OnUpdateConnectionStatus()
  801. {
  802. auto* statusBar = StatusBar();
  803. if (!m_connectionListener)
  804. {
  805. statusBar->SetItem("connection", tr("Disconnected"), tr("Disconnected"), IDI_BALL_DISABLED);
  806. //TODO: disable clicking
  807. }
  808. else
  809. {
  810. using EConnectionState = EngineConnectionListener::EConnectionState;
  811. int icon = IDI_BALL_OFFLINE;
  812. QString tooltip, status;
  813. switch (m_connectionListener->GetState())
  814. {
  815. case EConnectionState::Connecting:
  816. // Checking whether we are not connected here instead of disconnect state because this function is called on a timer
  817. // and therefore we may not receive the disconnect state.
  818. if (m_connectedToAssetProcessor)
  819. {
  820. m_connectedToAssetProcessor = false;
  821. m_showAPDisconnectDialog = true;
  822. }
  823. tooltip = tr("Connecting to Asset Processor");
  824. icon = IDI_BALL_PENDING;
  825. break;
  826. case EConnectionState::Disconnecting:
  827. tooltip = tr("Disconnecting from Asset Processor");
  828. icon = IDI_BALL_PENDING;
  829. break;
  830. case EConnectionState::Listening:
  831. if (m_connectedToAssetProcessor)
  832. {
  833. m_connectedToAssetProcessor = false;
  834. m_showAPDisconnectDialog = true;
  835. }
  836. tooltip = tr("Listening for incoming connections");
  837. icon = IDI_BALL_PENDING;
  838. break;
  839. case EConnectionState::Connected:
  840. m_connectedToAssetProcessor = true;
  841. tooltip = tr("Connected to Asset Processor");
  842. icon = IDI_BALL_ONLINE;
  843. break;
  844. case EConnectionState::Disconnected:
  845. icon = IDI_BALL_OFFLINE;
  846. tooltip = tr("Disconnected from Asset Processor");
  847. break;
  848. }
  849. if (m_connectedToAssetProcessor)
  850. {
  851. m_connectionLostTimer->stop();
  852. }
  853. tooltip += "\n Last Asset Processor Task: ";
  854. tooltip += m_connectionListener->LastAssetProcessorTask().c_str();
  855. tooltip += "\n";
  856. AZStd::set<AZStd::string> failedJobs = m_connectionListener->FailedJobsList();
  857. int failureCount = static_cast<int>(failedJobs.size());
  858. if (failureCount)
  859. {
  860. tooltip += "\n Failed Jobs\n";
  861. for (auto failedJob : failedJobs)
  862. {
  863. tooltip += failedJob.c_str();
  864. tooltip += "\n";
  865. }
  866. }
  867. status = tr("Pending Jobs : %1 Failed Jobs : %2").arg(m_connectionListener->GetJobsCount()).arg(failureCount);
  868. statusBar->SetItem("connection", status, tooltip, icon);
  869. if (m_showAPDisconnectDialog && m_connectionListener->GetState() != EConnectionState::Connected)
  870. {
  871. m_showAPDisconnectDialog = false;// Just show the dialog only once if connection is lost
  872. m_connectionLostTimer->setSingleShot(true);
  873. m_connectionLostTimer->start(15000);
  874. }
  875. }
  876. }
  877. void MainWindow::ShowConnectionDisconnectedDialog()
  878. {
  879. // when REMOTE_ASSET_PROCESSOR is undef'd it means behave as if there is no such thing as the remote asset processor.
  880. #ifdef REMOTE_ASSET_PROCESSOR
  881. if (gEnv && gEnv->pSystem)
  882. {
  883. QMessageBox messageBox(this);
  884. messageBox.setWindowTitle(tr("Asset Processor has disconnected."));
  885. messageBox.setText(
  886. tr("Asset Processor is not connected. Please try (re)starting the Asset Processor or restarting the Editor.<br><br>"
  887. "Data may be lost while the Asset Processor is not running!<br>"
  888. "The status of the Asset Processor can be monitored from the editor in the bottom-right corner of the status bar.<br><br>"
  889. "Would you like to start the asset processor?<br>"));
  890. messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Ignore);
  891. messageBox.setDefaultButton(QMessageBox::Yes);
  892. messageBox.setIcon(QMessageBox::Critical);
  893. if (messageBox.exec() == QMessageBox::Yes)
  894. {
  895. AzFramework::AssetSystem::LaunchAssetProcessor();
  896. }
  897. }
  898. else
  899. {
  900. QMessageBox::critical(this, tr("Asset Processor has disconnected."),
  901. tr("Asset Processor is not connected. Please try (re)starting the asset processor or restarting the Editor.<br><br>"
  902. "Data may be lost while the asset processor is not running!<br>"
  903. "The status of the asset processor can be monitored from the editor in the bottom-right corner of the status bar."));
  904. }
  905. #endif
  906. }
  907. void MainWindow::OnConnectionStatusClicked()
  908. {
  909. AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystemRequestBus::Events::ShowAssetProcessor);
  910. }
  911. static bool paneLessThan(const QtViewPane& v1, const QtViewPane& v2)
  912. {
  913. return v1.m_name.compare(v2.m_name, Qt::CaseInsensitive) < 0;
  914. }
  915. void MainWindow::RegisterOpenWndCommands()
  916. {
  917. s_openViewCmds.clear();
  918. auto panes = m_viewPaneManager->GetRegisteredPanes(/* viewPaneMenuOnly=*/ false);
  919. std::sort(panes.begin(), panes.end(), paneLessThan);
  920. for (auto viewPane : panes)
  921. {
  922. if (viewPane.m_category.isEmpty())
  923. {
  924. continue;
  925. }
  926. const QString className = viewPane.m_name;
  927. // Make a open-view command for the class.
  928. QString classNameLowered = viewPane.m_name.toLower();
  929. classNameLowered.replace(' ', '_');
  930. QString openCommandName = "open_";
  931. openCommandName += classNameLowered;
  932. CEditorOpenViewCommand* pCmd = new CEditorOpenViewCommand(GetIEditor(), viewPane.m_name);
  933. s_openViewCmds.push_back(pCmd);
  934. CCommand0::SUIInfo cmdUI;
  935. cmdUI.caption = className.toUtf8().data();
  936. cmdUI.tooltip = (QString("Open ") + className).toUtf8().data();
  937. cmdUI.iconFilename = className.toUtf8().data();
  938. GetIEditor()->GetCommandManager()->RegisterUICommand("editor", openCommandName.toUtf8().data(),
  939. "", "", [pCmd] { pCmd->Execute(); }, cmdUI);
  940. GetIEditor()->GetCommandManager()->GetUIInfo("editor", openCommandName.toUtf8().data(), cmdUI);
  941. }
  942. }
  943. bool MainWindow::event(QEvent* event)
  944. {
  945. #ifdef Q_OS_MAC
  946. if (event->type() == QEvent::HoverMove)
  947. {
  948. // this fixes a problem on macOS where the mouse cursor was not
  949. // set when hovering over the splitter handles between dock widgets
  950. // it might be fixed in future Qt versions
  951. auto mouse = static_cast<QHoverEvent*>(event);
  952. bool result = QMainWindow::event(event);
  953. void setCocoaMouseCursor(QWidget*);
  954. setCocoaMouseCursor(childAt(mouse->pos()));
  955. return result;
  956. }
  957. #endif
  958. return QMainWindow::event(event);
  959. }
  960. void MainWindow::ToggleConsole()
  961. {
  962. m_viewPaneManager->TogglePane(LyViewPane::Console);
  963. QtViewPane* pane = m_viewPaneManager->GetPane(LyViewPane::Console);
  964. if (!pane)
  965. {
  966. return;
  967. }
  968. // If we toggled the console on, we want to focus its input text field
  969. if (pane->IsVisible())
  970. {
  971. CConsoleSCB* console = qobject_cast<CConsoleSCB*>(pane->Widget());
  972. if (!console)
  973. {
  974. return;
  975. }
  976. console->SetInputFocus();
  977. }
  978. }
  979. void MainWindow::ConnectivityStateChanged(const AzToolsFramework::SourceControlState state)
  980. {
  981. gSettings.enableSourceControl = (state == AzToolsFramework::SourceControlState::Active || state == AzToolsFramework::SourceControlState::ConfigurationInvalid);
  982. gSettings.SaveEnableSourceControlFlag(false);
  983. gSettings.SaveSettingsRegistryFile();
  984. }
  985. void MainWindow::OnGotoSelected()
  986. {
  987. AzToolsFramework::EditorRequestBus::Broadcast(&AzToolsFramework::EditorRequestBus::Events::GoToSelectedEntitiesInViewports);
  988. }
  989. // don't want to eat escape as if it were a shortcut, as it would eat it for other windows that also care about escape
  990. // and are reading it as an event rather.
  991. void MainWindow::keyPressEvent(QKeyEvent* e)
  992. {
  993. // We shouldn't need to do this, as there's already an escape key shortcut set on an action
  994. // attached to the MainWindow. We need to explicitly trap the escape key here because when in
  995. // Game Mode, all of the actions attached to the MainWindow are disabled.
  996. if (e->key() == Qt::Key_Escape)
  997. {
  998. MainWindow::OnEscapeAction();
  999. return;
  1000. }
  1001. return QMainWindow::keyPressEvent(e);
  1002. }
  1003. void MainWindow::dragEnterEvent(QDragEnterEvent *event)
  1004. {
  1005. using namespace AzQtComponents;
  1006. DragAndDropContextBase context;
  1007. DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::DragEnter, event, context);
  1008. }
  1009. void MainWindow::dragMoveEvent(QDragMoveEvent* event)
  1010. {
  1011. using namespace AzQtComponents;
  1012. DragAndDropContextBase context;
  1013. DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::DragMove, event, context);
  1014. }
  1015. void MainWindow::dragLeaveEvent(QDragLeaveEvent* event)
  1016. {
  1017. using namespace AzQtComponents;
  1018. DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::DragLeave, event);
  1019. }
  1020. void MainWindow::dropEvent(QDropEvent *event)
  1021. {
  1022. using namespace AzQtComponents;
  1023. DragAndDropContextBase context;
  1024. DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::Drop, event, context);
  1025. }
  1026. bool MainWindow::focusNextPrevChild(bool next)
  1027. {
  1028. // Don't change the focus when we're in game mode or else the viewport could
  1029. // stop receiving input events
  1030. if (GetIEditor()->IsInGameMode())
  1031. {
  1032. return false;
  1033. }
  1034. return QMainWindow::focusNextPrevChild(next);
  1035. }
  1036. namespace AzToolsFramework
  1037. {
  1038. void MainWindowEditorFuncsHandler::Reflect(AZ::ReflectContext* context)
  1039. {
  1040. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  1041. {
  1042. // this will put these methods into the 'azlmbr.legacy.general' module
  1043. auto addLegacyGeneral = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
  1044. {
  1045. methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  1046. ->Attribute(AZ::Script::Attributes::Category, "Legacy/Editor")
  1047. ->Attribute(AZ::Script::Attributes::Module, "legacy.general");
  1048. };
  1049. addLegacyGeneral(behaviorContext->Method("open_pane", PyOpenViewPane, nullptr, "Opens a view pane specified by the pane class name."));
  1050. addLegacyGeneral(behaviorContext->Method("close_pane", PyCloseViewPane, nullptr, "Closes a view pane specified by the pane class name."));
  1051. addLegacyGeneral(behaviorContext->Method("is_pane_visible", PyIsViewPaneVisible, nullptr, "Returns true if pane specified by the pane class name is visible."));
  1052. addLegacyGeneral(behaviorContext->Method("get_pane_class_names", PyGetViewPaneNames, nullptr, "Get all available class names for use with open_pane & close_pane."));
  1053. addLegacyGeneral(behaviorContext->Method("exit", PyExit, nullptr, "Exits the editor."));
  1054. addLegacyGeneral(behaviorContext->Method("exit_no_prompt", PyExitNoPrompt, nullptr, "Exits the editor without prompting to save first."));
  1055. addLegacyGeneral(behaviorContext->Method("test_output", PyTestOutput, nullptr, "Report test information."));
  1056. }
  1057. }
  1058. }
  1059. #include <moc_MainWindow.cpp>