MainWindow.cpp 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959
  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. // AWs Native SDK
  12. AZ_PUSH_DISABLE_WARNING(4251 4355 4996, "-Wunknown-warning-option")
  13. #include <aws/core/auth/AWSCredentialsProvider.h>
  14. AZ_POP_DISABLE_WARNING
  15. // Qt
  16. #include <QMenuBar>
  17. #include <QDebug>
  18. #include <QMessageBox>
  19. #include <QInputDialog>
  20. #include <QHBoxLayout>
  21. #ifdef Q_OS_WIN
  22. #include <QAbstractEventDispatcher>
  23. #endif
  24. // AzCore
  25. #include <AzCore/std/smart_ptr/make_shared.h>
  26. #include <AzCore/Component/ComponentApplication.h>
  27. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  28. #include <AzCore/Interface/Interface.h>
  29. #include <AzCore/Utils/Utils.h>
  30. // AzFramework
  31. #include <AzFramework/API/ApplicationAPI.h>
  32. #include <AzFramework/Asset/AssetSystemBus.h>
  33. #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
  34. #include <AzFramework/Network/SocketConnection.h>
  35. #include <AzFramework/Asset/AssetSystemComponent.h>
  36. // AzToolsFramework
  37. #include <AzToolsFramework/Application/Ticker.h>
  38. #include <AzToolsFramework/API/EditorWindowRequestBus.h>
  39. #include <AzToolsFramework/API/EditorAnimationSystemRequestBus.h>
  40. #include <AzToolsFramework/SourceControl/QtSourceControlNotificationHandler.h>
  41. #include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
  42. // AzQtComponents
  43. #include <AzQtComponents/Buses/ShortcutDispatch.h>
  44. #include <AzQtComponents/Components/DockMainWindow.h>
  45. #include <AzQtComponents/Components/Widgets/SpinBox.h>
  46. #include <AzQtComponents/Components/Style.h>
  47. #include <AzQtComponents/Components/WindowDecorationWrapper.h>
  48. #include <AzQtComponents/DragAndDrop/MainWindowDragAndDrop.h>
  49. // Editor
  50. #include "Resource.h"
  51. #include "Core/LevelEditorMenuHandler.h"
  52. #include "ShortcutDispatcher.h"
  53. #include "LayoutWnd.h"
  54. #include "AssetImporter/AssetImporterManager/AssetImporterManager.h"
  55. #include "AssetImporter/AssetImporterManager/AssetImporterDragAndDropHandler.h"
  56. #include "CryEdit.h"
  57. #include "Controls/ConsoleSCB.h"
  58. #include "ViewManager.h"
  59. #include "CryEditDoc.h"
  60. #include "ToolBox.h"
  61. #include "LevelIndependentFileMan.h"
  62. #include "GameEngine.h"
  63. #include "MainStatusBar.h"
  64. #include "ToolbarCustomizationDialog.h"
  65. #include "ToolbarManager.h"
  66. #include "Core/QtEditorApplication.h"
  67. #include "UndoDropDown.h"
  68. #include "CVarMenu.h"
  69. #include "EditorViewportSettings.h"
  70. #include "KeyboardCustomizationSettings.h"
  71. #include "CustomizeKeyboardDialog.h"
  72. #include "QtViewPaneManager.h"
  73. #include "ViewPane.h"
  74. #include "Include/IObjectManager.h"
  75. #include "Include/Command.h"
  76. #include "Commands/CommandManager.h"
  77. #include "SettingsManagerDialog.h"
  78. #include "TrackView/TrackViewDialog.h"
  79. #include "ErrorReportDialog.h"
  80. #include "Dialogs/PythonScriptsDialog.h"
  81. #include "AzAssetBrowser/AzAssetBrowserWindow.h"
  82. #include "AssetEditor/AssetEditorWindow.h"
  83. #include "ActionManager.h"
  84. #include <ImGuiBus.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. static const char* g_openViewPaneEventName = "OpenViewPaneEvent"; //Sent when users open view panes;
  93. static const char* g_viewPaneAttributeName = "ViewPaneName"; //Name of the current view pane
  94. static const char* g_openLocationAttributeName = "OpenLocation"; //Indicates where the current view pane is opened from
  95. static const char* g_assetImporterName = "AssetImporter";
  96. class CEditorOpenViewCommand
  97. : public _i_reference_target_t
  98. {
  99. QString m_className;
  100. public:
  101. CEditorOpenViewCommand(IEditor* pEditor, const QString& className)
  102. : m_pEditor(pEditor)
  103. , m_className(className)
  104. {
  105. assert(m_pEditor);
  106. }
  107. void Execute()
  108. {
  109. // Create browse mode for this category.
  110. m_pEditor->OpenView(m_className);
  111. }
  112. private:
  113. IEditor* m_pEditor;
  114. };
  115. namespace
  116. {
  117. // The purpose of this vector is just holding shared pointers, so CEditorOpenViewCommand dtors are called at exit
  118. std::vector<_smart_ptr<CEditorOpenViewCommand> > s_openViewCmds;
  119. }
  120. class EngineConnectionListener
  121. : public AzFramework::EngineConnectionEvents::Bus::Handler
  122. , public AzFramework::AssetSystemInfoBus::Handler
  123. {
  124. public:
  125. using EConnectionState = AzFramework::SocketConnection::EConnectionState;
  126. public:
  127. EngineConnectionListener()
  128. : m_state(EConnectionState::Disconnected)
  129. {
  130. AzFramework::EngineConnectionEvents::Bus::Handler::BusConnect();
  131. AzFramework::AssetSystemInfoBus::Handler::BusConnect();
  132. AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
  133. if (engineConnection)
  134. {
  135. m_state = engineConnection->GetConnectionState();
  136. }
  137. }
  138. ~EngineConnectionListener()
  139. {
  140. AzFramework::AssetSystemInfoBus::Handler::BusDisconnect();
  141. AzFramework::EngineConnectionEvents::Bus::Handler::BusDisconnect();
  142. }
  143. public:
  144. virtual void Connected([[maybe_unused]] AzFramework::SocketConnection* connection)
  145. {
  146. m_state = EConnectionState::Connected;
  147. }
  148. virtual void Connecting([[maybe_unused]] AzFramework::SocketConnection* connection)
  149. {
  150. m_state = EConnectionState::Connecting;
  151. }
  152. virtual void Listening([[maybe_unused]] AzFramework::SocketConnection* connection)
  153. {
  154. m_state = EConnectionState::Listening;
  155. }
  156. virtual void Disconnecting([[maybe_unused]] AzFramework::SocketConnection* connection)
  157. {
  158. m_state = EConnectionState::Disconnecting;
  159. }
  160. virtual void Disconnected([[maybe_unused]] AzFramework::SocketConnection* connection)
  161. {
  162. m_state = EConnectionState::Disconnected;
  163. }
  164. virtual void AssetCompilationSuccess(const AZStd::string& assetPath) override
  165. {
  166. m_lastAssetProcessorTask = assetPath;
  167. }
  168. virtual void AssetCompilationFailed(const AZStd::string& assetPath) override
  169. {
  170. m_failedJobs.insert(assetPath);
  171. }
  172. virtual void CountOfAssetsInQueue(const int& count) override
  173. {
  174. m_pendingJobsCount = count;
  175. }
  176. int GetJobsCount() const
  177. {
  178. return m_pendingJobsCount;
  179. }
  180. AZStd::set<AZStd::string> FailedJobsList() const
  181. {
  182. return m_failedJobs;
  183. }
  184. AZStd::string LastAssetProcessorTask() const
  185. {
  186. return m_lastAssetProcessorTask;
  187. }
  188. public:
  189. EConnectionState GetState() const
  190. {
  191. return m_state;
  192. }
  193. private:
  194. EConnectionState m_state;
  195. int m_pendingJobsCount = 0;
  196. AZStd::set<AZStd::string> m_failedJobs;
  197. AZStd::string m_lastAssetProcessorTask;
  198. };
  199. namespace
  200. {
  201. void PyOpenViewPane(const char* viewClassName)
  202. {
  203. QtViewPaneManager::instance()->OpenPane(viewClassName);
  204. }
  205. void PyCloseViewPane(const char* viewClassName)
  206. {
  207. QtViewPaneManager::instance()->ClosePane(viewClassName);
  208. }
  209. bool PyIsViewPaneVisible(const char* viewClassName)
  210. {
  211. return QtViewPaneManager::instance()->IsVisible(viewClassName);
  212. }
  213. AZStd::vector<AZStd::string> PyGetViewPaneNames()
  214. {
  215. const QtViewPanes panes = QtViewPaneManager::instance()->GetRegisteredPanes();
  216. AZStd::vector<AZStd::string> names;
  217. names.reserve(panes.size());
  218. AZStd::transform(panes.begin(), panes.end(), AZStd::back_inserter(names), [](const QtViewPane& pane)
  219. {
  220. return AZStd::string(pane.m_name.toUtf8().constData());
  221. });
  222. return names;
  223. }
  224. void PyExit()
  225. {
  226. // Adding a single-shot QTimer to PyExit delays the QApplication::closeAllWindows call until
  227. // all the events in the event queue have been processed. Calling QApplication::closeAllWindows instead
  228. // of MainWindow::close ensures the Metal render window is cleaned up on macOS.
  229. QTimer::singleShot(0, qApp, &QApplication::closeAllWindows);
  230. }
  231. void PyExitNoPrompt()
  232. {
  233. // Set the level to "unmodified" so that it doesn't prompt to save on exit.
  234. GetIEditor()->GetDocument()->SetModifiedFlag(false);
  235. PyExit();
  236. }
  237. void PyTestOutput(const AZStd::string& output)
  238. {
  239. CCryEditApp::instance()->PrintAlways(output);
  240. }
  241. }
  242. /////////////////////////////////////////////////////////////////////////////
  243. // MainWindow
  244. /////////////////////////////////////////////////////////////////////////////
  245. MainWindow* MainWindow::m_instance = nullptr;
  246. MainWindow::MainWindow(QWidget* parent)
  247. : QMainWindow(parent)
  248. , m_oldMainFrame(nullptr)
  249. , m_viewPaneManager(QtViewPaneManager::instance())
  250. , m_shortcutDispatcher(new ShortcutDispatcher(this))
  251. , m_actionManager(new ActionManager(this, QtViewPaneManager::instance(), m_shortcutDispatcher))
  252. , m_undoStateAdapter(new UndoStackStateAdapter(this))
  253. , m_keyboardCustomization(nullptr)
  254. , m_activeView(nullptr)
  255. , m_settings("O3DE", "O3DE")
  256. , m_toolbarManager(new ToolbarManager(m_actionManager, this))
  257. , m_assetImporterManager(new AssetImporterManager(this))
  258. , m_levelEditorMenuHandler(new LevelEditorMenuHandler(this, m_viewPaneManager, m_settings))
  259. , m_sourceControlNotifHandler(new AzToolsFramework::QtSourceControlNotificationHandler(this))
  260. , m_viewPaneHost(nullptr)
  261. , m_autoSaveTimer(nullptr)
  262. , m_autoRemindTimer(nullptr)
  263. , m_backgroundUpdateTimer(nullptr)
  264. , m_connectionLostTimer(new QTimer(this))
  265. {
  266. setObjectName("MainWindow"); // For IEditor::GetEditorMainWindow to work in plugins, where we can't link against MainWindow::instance()
  267. m_instance = this;
  268. //for new docking, create a DockMainWindow to host dock widgets so we can call QMainWindow::restoreState to restore docks without affecting our main toolbars.
  269. m_viewPaneHost = new AzQtComponents::DockMainWindow();
  270. m_viewPaneHost->setDockOptions(QMainWindow::GroupedDragging | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
  271. m_connectionListener = AZStd::make_shared<EngineConnectionListener>();
  272. QObject::connect(m_connectionLostTimer, &QTimer::timeout, this, &MainWindow::ShowConnectionDisconnectedDialog);
  273. setStatusBar(new MainStatusBar(this));
  274. setAttribute(Qt::WA_DeleteOnClose, true);
  275. connect(m_viewPaneManager, &QtViewPaneManager::viewPaneCreated, this, &MainWindow::OnViewPaneCreated);
  276. GetIEditor()->RegisterNotifyListener(this);
  277. AssetImporterDragAndDropHandler* assetImporterDragAndDropHandler = new AssetImporterDragAndDropHandler(this, m_assetImporterManager);
  278. connect(assetImporterDragAndDropHandler, &AssetImporterDragAndDropHandler::OpenAssetImporterManager, this, &MainWindow::OnOpenAssetImporterManager);
  279. connect(m_levelEditorMenuHandler, &LevelEditorMenuHandler::ActivateAssetImporter, this, [this]() {
  280. m_assetImporterManager->Exec();
  281. });
  282. setFocusPolicy(Qt::StrongFocus);
  283. setAcceptDrops(true);
  284. // special handling for escape key (outside ActionManager)
  285. auto* escapeAction = new QAction(this);
  286. escapeAction->setShortcut(QKeySequence(Qt::Key_Escape));
  287. addAction(escapeAction);
  288. connect(escapeAction, &QAction::triggered, this, &MainWindow::OnEscapeAction);
  289. const QSize minSize(800, 600);
  290. if (size().height() < minSize.height() || size().width() < minSize.width())
  291. {
  292. resize(size().expandedTo(minSize));
  293. }
  294. }
  295. void MainWindow::SystemTick()
  296. {
  297. AZ::ComponentApplication* componentApplication = nullptr;
  298. EBUS_EVENT_RESULT(componentApplication, AZ::ComponentApplicationBus, GetApplication);
  299. if (componentApplication)
  300. {
  301. componentApplication->TickSystem();
  302. }
  303. }
  304. #ifdef Q_OS_WIN
  305. HWND MainWindow::GetNativeHandle()
  306. {
  307. // if the parent widget is set, it's a window decoration wrapper
  308. // we use that instead, to ensure we're in lock step the code in CryEdit.cpp when it calls
  309. // InitGameSystem
  310. if (parentWidget() != nullptr)
  311. {
  312. assert(qobject_cast<AzQtComponents::WindowDecorationWrapper*>(parentWidget()));
  313. return QtUtil::getNativeHandle(parentWidget());
  314. }
  315. return QtUtil::getNativeHandle(this);
  316. }
  317. #endif // #ifdef Q_OS_WIN
  318. void MainWindow::OnOpenAssetImporterManager(const QStringList& dragAndDropFileList)
  319. {
  320. m_assetImporterManager->Exec(dragAndDropFileList);
  321. }
  322. CLayoutWnd* MainWindow::GetLayout() const
  323. {
  324. return m_pLayoutWnd;
  325. }
  326. CLayoutViewPane* MainWindow::GetActiveView() const
  327. {
  328. return m_activeView;
  329. }
  330. QtViewport* MainWindow::GetActiveViewport() const
  331. {
  332. return m_activeView ? qobject_cast<QtViewport*>(m_activeView->GetViewport()) : nullptr;
  333. }
  334. void MainWindow::SetActiveView(CLayoutViewPane* v)
  335. {
  336. m_activeView = v;
  337. }
  338. MainWindow::~MainWindow()
  339. {
  340. AzToolsFramework::SourceControlNotificationBus::Handler::BusDisconnect();
  341. delete m_toolbarManager;
  342. m_connectionListener.reset();
  343. GetIEditor()->UnregisterNotifyListener(this);
  344. // tear down the ActionOverride (clear the overrideWidget's parent)
  345. ActionOverrideRequestBus::Event(
  346. GetEntityContextId(), &ActionOverrideRequests::TeardownActionOverrideHandler);
  347. m_instance = nullptr;
  348. }
  349. void MainWindow::InitCentralWidget()
  350. {
  351. m_pLayoutWnd = new CLayoutWnd(&m_settings);
  352. // Set the central widgets before calling CreateLayout to avoid reparenting everything later
  353. setCentralWidget(m_viewPaneHost);
  354. m_viewPaneHost->setCentralWidget(m_pLayoutWnd);
  355. if (MainWindow::instance()->IsPreview())
  356. {
  357. m_pLayoutWnd->CreateLayout(ET_Layout0, true, ET_ViewportModel);
  358. }
  359. else
  360. {
  361. if (!m_pLayoutWnd->LoadConfig())
  362. {
  363. m_pLayoutWnd->CreateLayout(ET_Layout0);
  364. }
  365. }
  366. // make sure the layout wnd knows to reset it's layout and settings
  367. connect(m_viewPaneManager, &QtViewPaneManager::layoutReset, m_pLayoutWnd, &CLayoutWnd::ResetLayout);
  368. EBUS_EVENT(AzToolsFramework::EditorEvents::Bus, NotifyCentralWidgetInitialized);
  369. }
  370. void MainWindow::Initialize()
  371. {
  372. m_viewPaneManager->SetMainWindow(m_viewPaneHost, &m_settings, /*unused*/ QByteArray());
  373. RegisterStdViewClasses();
  374. InitCentralWidget();
  375. InitActions();
  376. // load toolbars ("shelves") and macros
  377. GetIEditor()->GetToolBoxManager()->Load(m_actionManager);
  378. InitToolActionHandlers();
  379. // Initialize toolbars before we setup the menu so that any tools can be added to the toolbar as needed
  380. InitToolBars();
  381. m_levelEditorMenuHandler->Initialize();
  382. InitStatusBar();
  383. AzToolsFramework::SourceControlNotificationBus::Handler::BusConnect();
  384. m_sourceControlNotifHandler->Init();
  385. m_keyboardCustomization = new KeyboardCustomizationSettings(QStringLiteral("Main Window"), this);
  386. if (!IsPreview())
  387. {
  388. RegisterOpenWndCommands();
  389. }
  390. ResetBackgroundUpdateTimer();
  391. ICVar* pBackgroundUpdatePeriod = gEnv->pConsole->GetCVar("ed_backgroundUpdatePeriod");
  392. if (pBackgroundUpdatePeriod)
  393. {
  394. pBackgroundUpdatePeriod->SetOnChangeCallback([](ICVar*) {
  395. MainWindow::instance()->ResetBackgroundUpdateTimer();
  396. });
  397. }
  398. // setup the ActionOverride (set overrideWidgets parent to be the MainWindow)
  399. ActionOverrideRequestBus::Event(
  400. GetEntityContextId(), &ActionOverrideRequests::SetupActionOverrideHandler, this);
  401. if (auto imGuiManager = AZ::Interface<ImGui::IImGuiManager>::Get())
  402. {
  403. auto handleImGuiStateChangeFn = [](bool enabled)
  404. {
  405. EditorWindowUIRequestBus::Broadcast(&EditorWindowUIRequests::SetEditorUiEnabled, enabled);
  406. };
  407. m_handleImGuiStateChangeHandler = ImGui::IImGuiManager::ImGuiSetEnabledEvent::Handler(handleImGuiStateChangeFn);
  408. imGuiManager->ConnectImGuiSetEnabledChangedHandler(m_handleImGuiStateChangeHandler);
  409. }
  410. AzToolsFramework::EditorEventsBus::Broadcast(&AzToolsFramework::EditorEvents::NotifyMainWindowInitialized, this);
  411. }
  412. void MainWindow::InitStatusBar()
  413. {
  414. StatusBar()->Init();
  415. connect(qobject_cast<StatusBarItem*>(StatusBar()->GetItem("connection")), &StatusBarItem::clicked, this, &MainWindow::OnConnectionStatusClicked);
  416. connect(StatusBar(), &MainStatusBar::requestStatusUpdate, this, &MainWindow::OnUpdateConnectionStatus);
  417. }
  418. CMainFrame* MainWindow::GetOldMainFrame() const
  419. {
  420. return m_oldMainFrame;
  421. }
  422. MainWindow* MainWindow::instance()
  423. {
  424. return m_instance;
  425. }
  426. void MainWindow::closeEvent(QCloseEvent* event)
  427. {
  428. gSettings.Save();
  429. AzFramework::SystemCursorState currentCursorState;
  430. bool isInGameMode = false;
  431. if (GetIEditor()->IsInGameMode())
  432. {
  433. isInGameMode = true;
  434. // Storecurrent state in case we need to restore Game Mode.
  435. AzFramework::InputSystemCursorRequestBus::EventResult(currentCursorState, AzFramework::InputDeviceMouse::Id,
  436. &AzFramework::InputSystemCursorRequests::GetSystemCursorState);
  437. // make sure the mouse is turned on before popping up any dialog boxes.
  438. AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
  439. &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
  440. AzFramework::SystemCursorState::UnconstrainedAndVisible);
  441. }
  442. if (GetIEditor()->GetDocument() && !GetIEditor()->GetDocument()->CanCloseFrame())
  443. {
  444. if (isInGameMode)
  445. {
  446. // make sure the mouse is turned back off if returning to the game.
  447. AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
  448. &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
  449. currentCursorState);
  450. }
  451. event->ignore();
  452. return;
  453. }
  454. KeyboardCustomizationSettings::EnableShortcutsGlobally(true);
  455. SaveConfig();
  456. // Some of the panes may ask for confirmation to save changes before closing.
  457. if (!QtViewPaneManager::instance()->ClosePanesWithRollback(QVector<QString>()) ||
  458. !GetIEditor() ||
  459. !GetIEditor()->GetLevelIndependentFileMan()->PromptChangedFiles())
  460. {
  461. if (isInGameMode)
  462. {
  463. // make sure the mouse is turned back off if returning to the game.
  464. AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
  465. &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
  466. currentCursorState);
  467. }
  468. event->ignore();
  469. return;
  470. }
  471. Editor::EditorQtApplication::instance()->EnableOnIdle(false);
  472. if (GetIEditor()->GetDocument())
  473. {
  474. GetIEditor()->GetDocument()->SetModifiedFlag(FALSE);
  475. GetIEditor()->GetDocument()->SetModifiedModules(eModifiedNothing);
  476. }
  477. // Close all edit panels.
  478. GetIEditor()->ClearSelection();
  479. GetIEditor()->GetObjectManager()->EndEditParams();
  480. // force clean up of all deferred deletes, so that we don't have any issues with windows from plugins not being deleted yet
  481. qApp->sendPostedEvents(0, QEvent::DeferredDelete);
  482. QMainWindow::closeEvent(event);
  483. }
  484. void MainWindow::SaveConfig()
  485. {
  486. m_settings.setValue("mainWindowState", saveState());
  487. QtViewPaneManager::instance()->SaveLayout();
  488. if (m_pLayoutWnd)
  489. {
  490. m_pLayoutWnd->SaveConfig();
  491. }
  492. GetIEditor()->GetToolBoxManager()->Save();
  493. }
  494. void MainWindow::ShowKeyboardCustomization()
  495. {
  496. CustomizeKeyboardDialog dialog(*m_keyboardCustomization, this);
  497. dialog.exec();
  498. }
  499. void MainWindow::ExportKeyboardShortcuts()
  500. {
  501. KeyboardCustomizationSettings::ExportToFile(this);
  502. }
  503. void MainWindow::ImportKeyboardShortcuts()
  504. {
  505. KeyboardCustomizationSettings::ImportFromFile(this);
  506. KeyboardCustomizationSettings::SaveGlobally();
  507. }
  508. void MainWindow::InitActions()
  509. {
  510. auto am = m_actionManager;
  511. auto cryEdit = CCryEditApp::instance();
  512. cryEdit->RegisterActionHandlers();
  513. am->AddAction(ID_TOOLBAR_SEPARATOR, QString());
  514. am->AddAction(ID_TOOLBAR_WIDGET_UNDO, QString());
  515. am->AddAction(ID_TOOLBAR_WIDGET_REDO, QString());
  516. am->AddAction(ID_TOOLBAR_WIDGET_SNAP_ANGLE, QString());
  517. am->AddAction(ID_TOOLBAR_WIDGET_SNAP_GRID, QString());
  518. am->AddAction(ID_TOOLBAR_WIDGET_SPACER_RIGHT, QString());
  519. // File actions
  520. am->AddAction(ID_FILE_NEW, tr("New Level"))
  521. .SetShortcut(tr("Ctrl+N"))
  522. .Connect(&QAction::triggered, [cryEdit]()
  523. {
  524. cryEdit->OnCreateLevel();
  525. })
  526. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateNewLevel);
  527. am->AddAction(ID_FILE_OPEN_LEVEL, tr("Open Level..."))
  528. .SetShortcut(tr("Ctrl+O"))
  529. .SetStatusTip(tr("Open an existing level"))
  530. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateFileOpen);
  531. #ifdef ENABLE_SLICE_EDITOR
  532. am->AddAction(ID_FILE_NEW_SLICE, tr("New Slice"))
  533. .SetStatusTip(tr("Create a new slice"));
  534. am->AddAction(ID_FILE_OPEN_SLICE, tr("Open Slice..."))
  535. .SetStatusTip(tr("Open an existing slice"));
  536. #endif
  537. am->AddAction(ID_FILE_SAVE_SELECTED_SLICE, tr("Save selected slice")).SetShortcut(tr("Alt+S"))
  538. .SetStatusTip(tr("Save the selected slice to the first level root"));
  539. am->AddAction(ID_FILE_SAVE_SLICE_TO_ROOT, tr("Save Slice to root")).SetShortcut(tr("Ctrl+Alt+S"))
  540. .SetStatusTip(tr("Save the selected slice to the top level root"));
  541. am->AddAction(ID_FILE_SAVE_LEVEL, tr("&Save"))
  542. .SetShortcut(tr("Ctrl+S"))
  543. .SetReserved()
  544. .SetStatusTip(tr("Save the current level"))
  545. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateDocumentReady);
  546. am->AddAction(ID_FILE_SAVE_AS, tr("Save &As..."))
  547. .SetShortcut(tr("Ctrl+Shift+S"))
  548. .SetReserved()
  549. .SetStatusTip(tr("Save the active document with a new name"))
  550. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateDocumentReady);
  551. am->AddAction(ID_FILE_SAVELEVELRESOURCES, tr("Save Level Resources..."))
  552. .SetStatusTip(tr("Save Resources"))
  553. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateDocumentReady);
  554. am->AddAction(ID_IMPORT_ASSET, tr("Import &FBX..."));
  555. bool usePrefabSystemForLevels = false;
  556. AzFramework::ApplicationRequests::Bus::BroadcastResult(
  557. usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
  558. if (!usePrefabSystemForLevels)
  559. {
  560. am->AddAction(ID_FILE_EXPORTTOGAMENOSURFACETEXTURE, tr("&Export to Engine"))
  561. .SetShortcut(tr("Ctrl+E"))
  562. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateDocumentReady);
  563. }
  564. am->AddAction(ID_FILE_EXPORT_SELECTEDOBJECTS, tr("Export Selected &Objects"))
  565. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateSelected);
  566. am->AddAction(ID_FILE_EXPORTOCCLUSIONMESH, tr("Export Occlusion Mesh"));
  567. am->AddAction(ID_FILE_EDITLOGFILE, tr("Show Log File"));
  568. am->AddAction(ID_FILE_RESAVESLICES, tr("Resave All Slices"));
  569. am->AddAction(ID_FILE_PROJECT_MANAGER_SETTINGS, tr("Edit Project Settings..."));
  570. am->AddAction(ID_FILE_PROJECT_MANAGER_NEW, tr("New Project..."));
  571. am->AddAction(ID_FILE_PROJECT_MANAGER_OPEN, tr("Open Project..."));
  572. am->AddAction(ID_TOOLS_CUSTOMIZEKEYBOARD, tr("Customize &Keyboard..."))
  573. .Connect(&QAction::triggered, this, &MainWindow::ShowKeyboardCustomization);
  574. am->AddAction(ID_TOOLS_EXPORT_SHORTCUTS, tr("&Export Keyboard Settings..."))
  575. .Connect(&QAction::triggered, this, &MainWindow::ExportKeyboardShortcuts);
  576. am->AddAction(ID_TOOLS_IMPORT_SHORTCUTS, tr("&Import Keyboard Settings..."))
  577. .Connect(&QAction::triggered, this, &MainWindow::ImportKeyboardShortcuts);
  578. am->AddAction(ID_TOOLS_PREFERENCES, tr("Global Preferences..."));
  579. for (int i = ID_FILE_MRU_FIRST; i <= ID_FILE_MRU_LAST; ++i)
  580. {
  581. am->AddAction(i, QString());
  582. }
  583. #if AZ_TRAIT_OS_PLATFORM_APPLE
  584. const QString appExitText = tr("&Quit");
  585. #else
  586. const QString appExitText = tr("E&xit");
  587. #endif
  588. am->AddAction(ID_APP_EXIT, appExitText)
  589. .SetReserved();
  590. // Edit actions
  591. am->AddAction(ID_UNDO, tr("&Undo"))
  592. .SetShortcut(QKeySequence::Undo)
  593. .SetReserved()
  594. .SetStatusTip(tr("Undo last operation"))
  595. //.SetMenu(new QMenu("FIXME"))
  596. .SetApplyHoverEffect()
  597. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateUndo);
  598. am->AddAction(ID_REDO, tr("&Redo"))
  599. .SetShortcut(AzQtComponents::RedoKeySequence)
  600. .SetReserved()
  601. //.SetMenu(new QMenu("FIXME"))
  602. .SetApplyHoverEffect()
  603. .SetStatusTip(tr("Redo last undo operation"))
  604. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateRedo);
  605. am->AddAction(ID_EDIT_HOLD, tr("&Hold"))
  606. .SetShortcut(tr("Ctrl+Alt+H"))
  607. .SetToolTip(tr("&Hold (Ctrl+Alt+H)"))
  608. .SetStatusTip(tr("Save the current state(Hold)"));
  609. am->AddAction(ID_EDIT_FETCH, tr("&Fetch"))
  610. .SetShortcut(tr("Ctrl+Alt+F"))
  611. .SetToolTip(tr("&Fetch (Ctrl+Alt+F)"))
  612. .SetStatusTip(tr("Restore saved state (Fetch)"));
  613. // Modify actions
  614. am->AddAction(ID_EDITMODE_MOVE, tr("Move"))
  615. .SetIcon(Style::icon("Move"))
  616. .SetApplyHoverEffect()
  617. .SetShortcut(tr("1"))
  618. .SetToolTip(tr("Move (1)"))
  619. .SetCheckable(true)
  620. .SetStatusTip(tr("Select and move selected object(s)"))
  621. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateEditmodeMove);
  622. am->AddAction(ID_EDITMODE_ROTATE, tr("Rotate"))
  623. .SetIcon(Style::icon("Translate"))
  624. .SetApplyHoverEffect()
  625. .SetShortcut(tr("2"))
  626. .SetToolTip(tr("Rotate (2)"))
  627. .SetCheckable(true)
  628. .SetStatusTip(tr("Select and rotate selected object(s)"))
  629. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateEditmodeRotate);
  630. am->AddAction(ID_EDITMODE_SCALE, tr("Scale"))
  631. .SetIcon(Style::icon("Scale"))
  632. .SetApplyHoverEffect()
  633. .SetShortcut(tr("3"))
  634. .SetToolTip(tr("Scale (3)"))
  635. .SetCheckable(true)
  636. .SetStatusTip(tr("Select and scale selected object(s)"))
  637. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateEditmodeScale);
  638. am->AddAction(ID_SNAP_TO_GRID, tr("Snap to grid"))
  639. .SetIcon(Style::icon("Grid"))
  640. .SetApplyHoverEffect()
  641. .SetShortcut(tr("G"))
  642. .SetToolTip(tr("Snap to grid (G)"))
  643. .SetStatusTip(tr("Toggles snap to grid"))
  644. .SetCheckable(true)
  645. .RegisterUpdateCallback([](QAction* action) {
  646. Q_ASSERT(action->isCheckable());
  647. action->setChecked(SandboxEditor::GridSnappingEnabled());
  648. })
  649. .Connect(&QAction::triggered, []() { SandboxEditor::SetGridSnapping(!SandboxEditor::GridSnappingEnabled()); });
  650. am->AddAction(ID_SNAPANGLE, tr("Snap angle"))
  651. .SetIcon(Style::icon("Angle"))
  652. .SetApplyHoverEffect()
  653. .SetStatusTip(tr("Snap angle"))
  654. .SetCheckable(true)
  655. .RegisterUpdateCallback([](QAction* action) {
  656. Q_ASSERT(action->isCheckable());
  657. action->setChecked(SandboxEditor::AngleSnappingEnabled());
  658. })
  659. .Connect(&QAction::triggered, []() { SandboxEditor::SetAngleSnapping(!SandboxEditor::AngleSnappingEnabled()); });
  660. // Display actions
  661. am->AddAction(ID_SWITCHCAMERA_DEFAULTCAMERA, tr("Default Camera")).SetCheckable(true)
  662. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateSwitchToDefaultCamera);
  663. am->AddAction(ID_SWITCHCAMERA_SEQUENCECAMERA, tr("Sequence Camera")).SetCheckable(true)
  664. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateSwitchToSequenceCamera);
  665. am->AddAction(ID_SWITCHCAMERA_SELECTEDCAMERA, tr("Selected Camera Object")).SetCheckable(true)
  666. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateSwitchToSelectedCamera);
  667. am->AddAction(ID_SWITCHCAMERA_NEXT, tr("Cycle Camera"))
  668. .SetShortcut(tr("Ctrl+`"))
  669. .SetToolTip(tr("Cycle Camera (Ctrl+`)"));
  670. am->AddAction(ID_DISPLAY_GOTOPOSITION, tr("Go to Position..."));
  671. am->AddAction(ID_MODIFY_GOTO_SELECTION, tr("Center on Selection"))
  672. .SetShortcut(tr("Z"))
  673. .SetToolTip(tr("Center on Selection (Z)"))
  674. .Connect(&QAction::triggered, this, &MainWindow::OnGotoSelected);
  675. am->AddAction(ID_GOTO_LOC1, tr("Location 1"))
  676. .SetShortcut(tr("Shift+F1"))
  677. .SetToolTip(tr("Location 1 (Shift+F1)"));
  678. am->AddAction(ID_GOTO_LOC2, tr("Location 2"))
  679. .SetShortcut(tr("Shift+F2"))
  680. .SetToolTip(tr("Location 2 (Shift+F2)"));
  681. am->AddAction(ID_GOTO_LOC3, tr("Location 3"))
  682. .SetShortcut(tr("Shift+F3"))
  683. .SetToolTip(tr("Location 3 (Shift+F3)"));
  684. am->AddAction(ID_GOTO_LOC4, tr("Location 4"))
  685. .SetShortcut(tr("Shift+F4"))
  686. .SetToolTip(tr("Location 4 (Shift+F4)"));
  687. am->AddAction(ID_GOTO_LOC5, tr("Location 5"))
  688. .SetShortcut(tr("Shift+F5"))
  689. .SetToolTip(tr("Location 5 (Shift+F5)"));
  690. am->AddAction(ID_GOTO_LOC6, tr("Location 6"))
  691. .SetShortcut(tr("Shift+F6"))
  692. .SetToolTip(tr("Location 6 (Shift+F6)"));
  693. am->AddAction(ID_GOTO_LOC7, tr("Location 7"))
  694. .SetShortcut(tr("Shift+F7"))
  695. .SetToolTip(tr("Location 7 (Shift+F7)"));
  696. am->AddAction(ID_GOTO_LOC8, tr("Location 8"))
  697. .SetShortcut(tr("Shift+F8"))
  698. .SetToolTip(tr("Location 8 (Shift+F8)"));
  699. am->AddAction(ID_GOTO_LOC9, tr("Location 9"))
  700. .SetShortcut(tr("Shift+F9"))
  701. .SetToolTip(tr("Location 9 (Shift+F9)"));
  702. am->AddAction(ID_GOTO_LOC10, tr("Location 10"))
  703. .SetShortcut(tr("Shift+F10"))
  704. .SetToolTip(tr("Location 10 (Shift+F10)"));
  705. am->AddAction(ID_GOTO_LOC11, tr("Location 11"))
  706. .SetShortcut(tr("Shift+F11"))
  707. .SetToolTip(tr("Location 11 (Shift+F11)"));
  708. am->AddAction(ID_GOTO_LOC12, tr("Location 12"))
  709. .SetShortcut(tr("Shift+F12"))
  710. .SetToolTip(tr("Location 12 (Shift+F12)"));
  711. am->AddAction(ID_TAG_LOC1, tr("Location 1"))
  712. .SetShortcut(tr("Ctrl+F1"))
  713. .SetToolTip(tr("Location 1 (Ctrl+F1)"));
  714. am->AddAction(ID_TAG_LOC2, tr("Location 2"))
  715. .SetShortcut(tr("Ctrl+F2"))
  716. .SetToolTip(tr("Location 2 (Ctrl+F2)"));
  717. am->AddAction(ID_TAG_LOC3, tr("Location 3"))
  718. .SetShortcut(tr("Ctrl+F3"))
  719. .SetToolTip(tr("Location 3 (Ctrl+F3)"));
  720. am->AddAction(ID_TAG_LOC4, tr("Location 4"))
  721. .SetShortcut(tr("Ctrl+F4"))
  722. .SetToolTip(tr("Location 4 (Ctrl+F4)"));
  723. am->AddAction(ID_TAG_LOC5, tr("Location 5"))
  724. .SetShortcut(tr("Ctrl+F5"))
  725. .SetToolTip(tr("Location 5 (Ctrl+F5)"));
  726. am->AddAction(ID_TAG_LOC6, tr("Location 6"))
  727. .SetShortcut(tr("Ctrl+F6"))
  728. .SetToolTip(tr("Location 6 (Ctrl+F6)"));
  729. am->AddAction(ID_TAG_LOC7, tr("Location 7"))
  730. .SetShortcut(tr("Ctrl+F7"))
  731. .SetToolTip(tr("Location 7 (Ctrl+F7)"));
  732. am->AddAction(ID_TAG_LOC8, tr("Location 8"))
  733. .SetShortcut(tr("Ctrl+F8"))
  734. .SetToolTip(tr("Location 8 (Ctrl+F8)"));
  735. am->AddAction(ID_TAG_LOC9, tr("Location 9"))
  736. .SetShortcut(tr("Ctrl+F9"))
  737. .SetToolTip(tr("Location 9 (Ctrl+F9)"));
  738. am->AddAction(ID_TAG_LOC10, tr("Location 10"))
  739. .SetShortcut(tr("Ctrl+F10"))
  740. .SetToolTip(tr("Location 10 (Ctrl+F10)"));
  741. am->AddAction(ID_TAG_LOC11, tr("Location 11"))
  742. .SetShortcut(tr("Ctrl+F11"))
  743. .SetToolTip(tr("Location 11 (Ctrl+F11)"));
  744. am->AddAction(ID_TAG_LOC12, tr("Location 12"))
  745. .SetShortcut(tr("Ctrl+F12"))
  746. .SetToolTip(tr("Location 12 (Ctrl+F12)"));
  747. if (CViewManager::IsMultiViewportEnabled())
  748. {
  749. am->AddAction(ID_VIEW_CONFIGURELAYOUT, tr("Configure Layout..."));
  750. }
  751. #ifdef FEATURE_ORTHOGRAPHIC_VIEW
  752. am->AddAction(ID_VIEW_CYCLE2DVIEWPORT, tr("Cycle Viewports"))
  753. .SetShortcut(tr("Ctrl+Tab"))
  754. .SetStatusTip(tr("Cycle 2D Viewport"))
  755. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateNonGameMode);
  756. #endif
  757. am->AddAction(ID_DISPLAY_SHOWHELPERS, tr("Show/Hide Helpers"))
  758. .SetShortcut(tr("Shift+Space"))
  759. .SetToolTip(tr("Show/Hide Helpers (Shift+Space)"));
  760. // Audio actions
  761. am->AddAction(ID_SOUND_STOPALLSOUNDS, tr("Stop All Sounds"))
  762. .Connect(&QAction::triggered, this, &MainWindow::OnStopAllSounds);
  763. am->AddAction(ID_AUDIO_REFRESH_AUDIO_SYSTEM, tr("Refresh Audio"))
  764. .Connect(&QAction::triggered, this, &MainWindow::OnRefreshAudioSystem);
  765. // Game actions
  766. am->AddAction(ID_VIEW_SWITCHTOGAME, tr("Play &Game"))
  767. .SetIcon(QIcon(":/stylesheet/img/UI20/toolbar/Play.svg"))
  768. .SetShortcut(tr("Ctrl+G"))
  769. .SetToolTip(tr("Play Game (Ctrl+G)"))
  770. .SetStatusTip(tr("Activate the game input mode"))
  771. .SetApplyHoverEffect()
  772. .SetCheckable(true)
  773. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdatePlayGame);
  774. am->AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, tr("Play &Game (Maximized)"))
  775. .SetShortcut(tr("Ctrl+Shift+G"))
  776. .SetStatusTip(tr("Activate the game input mode (maximized)"))
  777. .SetIcon(Style::icon("Play"))
  778. .SetApplyHoverEffect()
  779. .SetCheckable(true);
  780. am->AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, tr("Play Controls"))
  781. .SetText(tr("Play Controls"));
  782. am->AddAction(ID_SWITCH_PHYSICS, tr("Simulate"))
  783. .SetIcon(QIcon(":/stylesheet/img/UI20/toolbar/Simulate_Physics.svg"))
  784. .SetShortcut(tr("Ctrl+P"))
  785. .SetToolTip(tr("Simulate (Ctrl+P)"))
  786. .SetCheckable(true)
  787. .SetStatusTip(tr("Enable processing of Physics and AI."))
  788. .SetApplyHoverEffect()
  789. .SetCheckable(true)
  790. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnSwitchPhysicsUpdate);
  791. am->AddAction(ID_GAME_SYNCPLAYER, tr("Move Player and Camera Separately")).SetCheckable(true)
  792. .SetStatusTip(tr("Move Player and Camera Separately"))
  793. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnSyncPlayerUpdate);
  794. // Physics actions
  795. am->AddAction(ID_PHYSICS_GETPHYSICSSTATE, tr("Get Physics State"))
  796. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateSelected);
  797. am->AddAction(ID_PHYSICS_RESETPHYSICSSTATE, tr("Reset Physics State"))
  798. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateSelected);
  799. am->AddAction(ID_PHYSICS_SIMULATEOBJECTS, tr("Simulate Objects"))
  800. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateSelected);
  801. // Tools actions
  802. am->AddAction(ID_TOOLS_ENABLEFILECHANGEMONITORING, tr("Enable File Change Monitoring"));
  803. am->AddAction(ID_CLEAR_REGISTRY, tr("Clear Registry Data"))
  804. .SetStatusTip(tr("Clear Registry Data"));
  805. am->AddAction(ID_VALIDATELEVEL, tr("&Check Level for Errors"))
  806. .SetStatusTip(tr("Validate Level"));
  807. am->AddAction(ID_TOOLS_VALIDATEOBJECTPOSITIONS, tr("Check Object Positions"));
  808. QAction* saveLevelStatsAction =
  809. am->AddAction(ID_TOOLS_LOGMEMORYUSAGE, tr("Save Level Statistics"))
  810. .SetStatusTip(tr("Logs Editor memory usage."));
  811. if( saveLevelStatsAction )
  812. {
  813. saveLevelStatsAction->setEnabled(false);
  814. }
  815. am->AddAction(ID_RESOURCES_REDUCEWORKINGSET, tr("Reduce Working Set"))
  816. .SetStatusTip(tr("Reduce Physical RAM Working Set."));
  817. am->AddAction(ID_TOOLS_UPDATEPROCEDURALVEGETATION, tr("Update Procedural Vegetation"));
  818. am->AddAction(ID_TOOLS_CONFIGURETOOLS, tr("Configure ToolBox Macros..."));
  819. am->AddAction(ID_TOOLS_SCRIPTHELP, tr("Script Help"));
  820. am->AddAction(ID_TOOLS_LUA_EDITOR, tr("Lua Editor"));
  821. // View actions
  822. am->AddAction(ID_VIEW_OPENVIEWPANE, tr("Open View Pane"));
  823. am->AddAction(ID_VIEW_CONSOLEWINDOW, tr(LyViewPane::ConsoleMenuName))
  824. .SetShortcut(tr("^"))
  825. .SetReserved()
  826. .SetStatusTip(tr("Show or hide the console window"))
  827. .SetCheckable(true)
  828. .Connect(&QAction::triggered, this, &MainWindow::ToggleConsole);
  829. am->AddAction(ID_OPEN_QUICK_ACCESS_BAR, tr("Show &Quick Access Bar"))
  830. .SetShortcut(tr("Ctrl+Alt+Space"))
  831. .SetToolTip(tr("Show &Quick Access Bar (Ctrl+Alt+Space)"));
  832. // Disable layouts menu
  833. if (CViewManager::IsMultiViewportEnabled())
  834. {
  835. am->AddAction(ID_VIEW_LAYOUTS, tr("Layouts"));
  836. am->AddAction(ID_VIEW_SAVELAYOUT, tr("Save Layout..."))
  837. .Connect(&QAction::triggered, this, &MainWindow::SaveLayout);
  838. am->AddAction(ID_VIEW_LAYOUT_LOAD_DEFAULT, tr("Restore Default Layout"))
  839. .Connect(&QAction::triggered, [this]() { m_viewPaneManager->RestoreDefaultLayout(true); });
  840. }
  841. am->AddAction(ID_SKINS_REFRESH, tr("Refresh Style"))
  842. .SetToolTip(tr("Refreshes the editor stylesheet"))
  843. .Connect(&QAction::triggered, this, &MainWindow::RefreshStyle);
  844. // Help actions
  845. am->AddAction(ID_DOCUMENTATION_TUTORIALS, tr("Tutorials"))
  846. .SetReserved();
  847. am->AddAction(ID_DOCUMENTATION_O3DE, tr("Open 3D Engine Documentation"))
  848. .SetReserved();
  849. am->AddAction(ID_DOCUMENTATION_GAMELIFT, tr("GameLift Documentation"))
  850. .SetReserved();
  851. am->AddAction(ID_DOCUMENTATION_RELEASENOTES, tr("Release Notes"))
  852. .SetReserved();
  853. am->AddAction(ID_DOCUMENTATION_GAMEDEVBLOG, tr("GameDev Blog"))
  854. .SetReserved();
  855. am->AddAction(ID_DOCUMENTATION_FORUMS, tr("Forums"))
  856. .SetReserved();
  857. am->AddAction(ID_DOCUMENTATION_AWSSUPPORT, tr("AWS Support"))
  858. .SetReserved();
  859. am->AddAction(ID_APP_ABOUT, tr("&About O3DE"))
  860. .SetStatusTip(tr("Display program information, version number and copyright"))
  861. .SetReserved();
  862. am->AddAction(ID_APP_SHOW_WELCOME, tr("&Welcome"))
  863. .SetStatusTip(tr("Show the Welcome to O3DE dialog box"))
  864. .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateShowWelcomeScreen);
  865. // Editors Toolbar actions
  866. am->AddAction(ID_OPEN_ASSET_BROWSER, tr("Asset browser"))
  867. .SetToolTip(tr("Open Asset Browser"))
  868. .SetApplyHoverEffect();
  869. AZ::EBusReduceResult<bool, AZStd::logical_or<bool>> emfxEnabled(false);
  870. using AnimationRequestBus = AzToolsFramework::EditorAnimationSystemRequestsBus;
  871. using AnimationSystemType = AzToolsFramework::EditorAnimationSystemRequests::AnimationSystem;
  872. AnimationRequestBus::BroadcastResult(emfxEnabled, &AnimationRequestBus::Events::IsSystemActive, AnimationSystemType::EMotionFX);
  873. if (emfxEnabled.value)
  874. {
  875. QAction* action = am->AddAction(ID_OPEN_EMOTIONFX_EDITOR, tr("Animation Editor"))
  876. .SetToolTip(tr("Open Animation Editor (PREVIEW)"))
  877. .SetIcon(QIcon(":/EMotionFX/EMFX_icon_32x32.png"))
  878. .SetApplyHoverEffect();
  879. QObject::connect(action, &QAction::triggered, this, []() {
  880. QtViewPaneManager::instance()->OpenPane(LyViewPane::AnimationEditor);
  881. });
  882. }
  883. am->AddAction(ID_OPEN_AUDIO_CONTROLS_BROWSER, tr("Audio Controls Editor"))
  884. .SetToolTip(tr("Open Audio Controls Editor"))
  885. .SetIcon(Style::icon("Audio"))
  886. .SetApplyHoverEffect();
  887. am->AddAction(ID_OPEN_UICANVASEDITOR, tr(LyViewPane::UiEditor))
  888. .SetToolTip(tr("Open UI Editor"))
  889. .SetApplyHoverEffect();
  890. // Edit Mode Toolbar Actions
  891. am->AddAction(IDC_SELECTION_MASK, tr("Selected Object Types"));
  892. am->AddAction(ID_REF_COORDS_SYS, tr("Reference coordinate system"))
  893. .SetShortcut(tr("Ctrl+W"))
  894. .SetToolTip(tr("Reference coordinate system (Ctrl+W)"))
  895. .Connect(&QAction::triggered, this, &MainWindow::ToggleRefCoordSys);
  896. am->AddAction(IDC_SELECTION, tr("Named Selections"));
  897. // Object Toolbar Actions
  898. am->AddAction(ID_GOTO_SELECTED, tr("Go to selected object"))
  899. .SetIcon(Style::icon("select_object"))
  900. .SetApplyHoverEffect()
  901. .Connect(&QAction::triggered, this, &MainWindow::OnGotoSelected);
  902. // Misc Toolbar Actions
  903. am->AddAction(ID_OPEN_SUBSTANCE_EDITOR, tr("Open Substance Editor"))
  904. .SetApplyHoverEffect();
  905. }
  906. void MainWindow::InitToolActionHandlers()
  907. {
  908. ActionManager* am = GetActionManager();
  909. CToolBoxManager* tbm = GetIEditor()->GetToolBoxManager();
  910. am->RegisterActionHandler(ID_APP_EXIT, [=]() { window()->close(); });
  911. for (int id = ID_TOOL_FIRST; id <= ID_TOOL_LAST; ++id)
  912. {
  913. am->RegisterActionHandler(id, [tbm, id] {
  914. tbm->ExecuteMacro(id - ID_TOOL_FIRST, true);
  915. });
  916. }
  917. for (int id = ID_TOOL_SHELVE_FIRST; id <= ID_TOOL_SHELVE_LAST; ++id)
  918. {
  919. am->RegisterActionHandler(id, [tbm, id] {
  920. tbm->ExecuteMacro(id - ID_TOOL_SHELVE_FIRST, false);
  921. });
  922. }
  923. for (int id = CEditorCommandManager::CUSTOM_COMMAND_ID_FIRST; id <= CEditorCommandManager::CUSTOM_COMMAND_ID_LAST; ++id)
  924. {
  925. am->RegisterActionHandler(id, [id] {
  926. GetIEditor()->GetCommandManager()->Execute(id);
  927. });
  928. }
  929. }
  930. void MainWindow::OnEscapeAction()
  931. {
  932. if (!CCryEditApp::instance()->IsInAutotestMode())
  933. {
  934. if (GetIEditor()->IsInGameMode())
  935. {
  936. GetIEditor()->SetInGameMode(false);
  937. }
  938. else
  939. {
  940. AzToolsFramework::EditorEvents::Bus::Broadcast(
  941. &AzToolsFramework::EditorEvents::OnEscape);
  942. }
  943. }
  944. }
  945. void MainWindow::InitToolBars()
  946. {
  947. m_toolbarManager->LoadToolbars();
  948. AdjustToolBarIconSize(static_cast<AzQtComponents::ToolBar::ToolBarIconSize>(gSettings.gui.nToolbarIconSize));
  949. }
  950. QToolButton* MainWindow::CreateUndoRedoButton(int command)
  951. {
  952. // We do either undo or redo below, sort that out here
  953. UndoRedoDirection direction = UndoRedoDirection::Undo;
  954. auto stateSignal = &UndoStackStateAdapter::UndoAvailable;
  955. if (ID_REDO == command)
  956. {
  957. direction = UndoRedoDirection::Redo;
  958. stateSignal = &UndoStackStateAdapter::RedoAvailable;
  959. }
  960. auto button = new UndoRedoToolButton(this);
  961. button->setAutoRaise(true);
  962. button->setPopupMode(QToolButton::MenuButtonPopup);
  963. button->setDefaultAction(m_actionManager->GetAction(command));
  964. QMenu* menu = new QMenu(button);
  965. auto action = new QWidgetAction(button);
  966. auto undoRedo = new CUndoDropDown(direction, button);
  967. action->setDefaultWidget(undoRedo);
  968. menu->addAction(action);
  969. button->setMenu(menu);
  970. connect(menu, &QMenu::aboutToShow, undoRedo, &CUndoDropDown::Prepare);
  971. connect(undoRedo, &CUndoDropDown::accepted, menu, &QMenu::hide);
  972. connect(m_undoStateAdapter, stateSignal, button, &UndoRedoToolButton::Update);
  973. button->setEnabled(false);
  974. return button;
  975. }
  976. QWidget* MainWindow::CreateSpacerRightWidget()
  977. {
  978. QWidget* spacer = new QWidget(this);
  979. spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
  980. spacer->setVisible(true);
  981. return spacer;
  982. }
  983. UndoRedoToolButton::UndoRedoToolButton(QWidget* parent)
  984. : QToolButton(parent)
  985. {
  986. }
  987. void UndoRedoToolButton::Update(int count)
  988. {
  989. setEnabled(count > 0);
  990. }
  991. bool MainWindow::IsPreview() const
  992. {
  993. return GetIEditor()->IsInPreviewMode();
  994. }
  995. MainStatusBar* MainWindow::StatusBar() const
  996. {
  997. assert(statusBar()->inherits("MainStatusBar"));
  998. return static_cast<MainStatusBar*>(statusBar());
  999. }
  1000. KeyboardCustomizationSettings* MainWindow::GetShortcutManager() const
  1001. {
  1002. return m_keyboardCustomization;
  1003. }
  1004. ActionManager* MainWindow::GetActionManager() const
  1005. {
  1006. return m_actionManager;
  1007. }
  1008. void MainWindow::OpenViewPane(int paneId)
  1009. {
  1010. OpenViewPane(QtViewPaneManager::instance()->GetPane(paneId));
  1011. }
  1012. void MainWindow::OpenViewPane(QtViewPane* pane)
  1013. {
  1014. if (pane && pane->IsValid())
  1015. {
  1016. QtViewPaneManager::instance()->OpenPane(pane->m_name);
  1017. }
  1018. else
  1019. {
  1020. if (pane)
  1021. {
  1022. qWarning() << Q_FUNC_INFO << "Invalid pane" << pane->m_id << pane->m_category << pane->m_name;
  1023. }
  1024. else
  1025. {
  1026. qWarning() << Q_FUNC_INFO << "Invalid pane";
  1027. }
  1028. }
  1029. }
  1030. void MainWindow::AdjustToolBarIconSize(AzQtComponents::ToolBar::ToolBarIconSize size)
  1031. {
  1032. const QList<QToolBar*> toolbars = findChildren<QToolBar*>();
  1033. // make sure to set this back, so that the general settings page matches up with what the size is too
  1034. if (gSettings.gui.nToolbarIconSize != static_cast<int>(size))
  1035. {
  1036. gSettings.gui.nToolbarIconSize = static_cast<int>(size);
  1037. }
  1038. for (auto toolbar : toolbars)
  1039. {
  1040. AzQtComponents::ToolBar::setToolBarIconSize(toolbar, size);
  1041. }
  1042. }
  1043. void MainWindow::OnGameModeChanged(bool inGameMode)
  1044. {
  1045. menuBar()->setDisabled(inGameMode);
  1046. m_toolbarManager->SetEnabled(!inGameMode);
  1047. // block signals on the switch to game actions before setting the checked state, as
  1048. // setting the checked state triggers the action, which will re-enter this function
  1049. // and result in an infinite loop
  1050. AZStd::vector<QAction*> actions = { m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME), m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN) };
  1051. for (auto action : actions)
  1052. {
  1053. action->blockSignals(true);
  1054. }
  1055. for (auto action : actions)
  1056. {
  1057. action->setChecked(inGameMode);
  1058. }
  1059. for (auto action : actions)
  1060. {
  1061. action->blockSignals(false);
  1062. }
  1063. }
  1064. void MainWindow::OnEditorNotifyEvent(EEditorNotifyEvent ev)
  1065. {
  1066. switch (ev)
  1067. {
  1068. case eNotify_OnEndSceneOpen:
  1069. case eNotify_OnEndSceneSave:
  1070. {
  1071. auto cryEdit = CCryEditApp::instance();
  1072. if (cryEdit)
  1073. {
  1074. cryEdit->SetEditorWindowTitle(0, AZ::Utils::GetProjectName().c_str(), GetIEditor()->GetGameEngine()->GetLevelName());
  1075. }
  1076. }
  1077. break;
  1078. case eNotify_OnCloseScene:
  1079. {
  1080. auto cryEdit = CCryEditApp::instance();
  1081. if (cryEdit)
  1082. {
  1083. cryEdit->SetEditorWindowTitle(0, AZ::Utils::GetProjectName().c_str(), 0);
  1084. }
  1085. }
  1086. break;
  1087. case eNotify_OnRefCoordSysChange:
  1088. emit UpdateRefCoordSys();
  1089. break;
  1090. case eNotify_OnInvalidateControls:
  1091. InvalidateControls();
  1092. break;
  1093. case eNotify_OnBeginGameMode:
  1094. OnGameModeChanged(true);
  1095. break;
  1096. case eNotify_OnEndGameMode:
  1097. OnGameModeChanged(false);
  1098. break;
  1099. // Remove track view option to avoid starting in bad state
  1100. case eNotify_OnBeginSimulationMode:
  1101. if (m_actionManager->HasAction(ID_OPEN_TRACKVIEW))
  1102. {
  1103. QAction* tvAction = m_actionManager->GetAction(ID_OPEN_TRACKVIEW);
  1104. if (tvAction)
  1105. {
  1106. tvAction->setVisible(false);
  1107. }
  1108. }
  1109. break;
  1110. case eNotify_OnEndSimulationMode:
  1111. if (m_actionManager->HasAction(ID_OPEN_TRACKVIEW))
  1112. {
  1113. QAction* tvAction = m_actionManager->GetAction(ID_OPEN_TRACKVIEW);
  1114. if (tvAction)
  1115. {
  1116. tvAction->setVisible(true);
  1117. }
  1118. }
  1119. break;
  1120. }
  1121. switch (ev)
  1122. {
  1123. case eNotify_OnBeginSceneOpen:
  1124. case eNotify_OnBeginNewScene:
  1125. case eNotify_OnCloseScene:
  1126. ResetAutoSaveTimers();
  1127. break;
  1128. case eNotify_OnEndSceneOpen:
  1129. case eNotify_OnEndNewScene:
  1130. ResetAutoSaveTimers(true);
  1131. break;
  1132. }
  1133. }
  1134. void MainWindow::InvalidateControls()
  1135. {
  1136. emit UpdateRefCoordSys();
  1137. }
  1138. void MainWindow::RegisterStdViewClasses()
  1139. {
  1140. AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(this);
  1141. CTrackViewDialog::RegisterViewClass();
  1142. CErrorReportDialog::RegisterViewClass();
  1143. CPythonScriptsDialog::RegisterViewClass();
  1144. AzToolsFramework::CScriptTermDialog::RegisterViewClass();
  1145. CConsoleSCB::RegisterViewClass();
  1146. ConsoleVariableEditor::RegisterViewClass();
  1147. CSettingsManagerDialog::RegisterViewClass();
  1148. AzAssetBrowserWindow::RegisterViewClass();
  1149. AssetEditorWindow::RegisterViewClass();
  1150. // Notify that views can now be registered
  1151. AzToolsFramework::EditorEvents::Bus::Broadcast(
  1152. &AzToolsFramework::EditorEvents::Bus::Events::NotifyRegisterViews);
  1153. }
  1154. void MainWindow::OnCustomizeToolbar()
  1155. {
  1156. /* TODO_KDAB, rest of CMainFrm::OnCustomize() goes here*/
  1157. SaveConfig();
  1158. }
  1159. void MainWindow::RefreshStyle()
  1160. {
  1161. GetIEditor()->Notify(eNotify_OnStyleChanged);
  1162. }
  1163. void MainWindow::ResetAutoSaveTimers(bool bForceInit)
  1164. {
  1165. if (m_autoSaveTimer)
  1166. {
  1167. delete m_autoSaveTimer;
  1168. }
  1169. if (m_autoRemindTimer)
  1170. {
  1171. delete m_autoRemindTimer;
  1172. }
  1173. m_autoSaveTimer = 0;
  1174. m_autoRemindTimer = 0;
  1175. if (bForceInit)
  1176. {
  1177. if (gSettings.autoBackupTime > 0 && gSettings.autoBackupEnabled)
  1178. {
  1179. m_autoSaveTimer = new QTimer(this);
  1180. m_autoSaveTimer->start(gSettings.autoBackupTime * 1000 * 60);
  1181. connect(m_autoSaveTimer, &QTimer::timeout, this, [&]() {
  1182. if (gSettings.autoBackupEnabled)
  1183. {
  1184. // Call autosave function of CryEditApp
  1185. GetIEditor()->GetDocument()->SaveAutoBackup();
  1186. }
  1187. });
  1188. }
  1189. if (gSettings.autoRemindTime > 0)
  1190. {
  1191. m_autoRemindTimer = new QTimer(this);
  1192. m_autoRemindTimer->start(gSettings.autoRemindTime * 1000 * 60);
  1193. connect(m_autoRemindTimer, &QTimer::timeout, this, [&]() {
  1194. if (gSettings.autoRemindTime > 0)
  1195. {
  1196. // Remind to save.
  1197. CCryEditApp::instance()->SaveAutoRemind();
  1198. }
  1199. });
  1200. }
  1201. }
  1202. }
  1203. void MainWindow::ResetBackgroundUpdateTimer()
  1204. {
  1205. if (m_backgroundUpdateTimer)
  1206. {
  1207. delete m_backgroundUpdateTimer;
  1208. m_backgroundUpdateTimer = 0;
  1209. }
  1210. ICVar* pBackgroundUpdatePeriod = gEnv->pConsole->GetCVar("ed_backgroundUpdatePeriod");
  1211. if (pBackgroundUpdatePeriod && pBackgroundUpdatePeriod->GetIVal() > 0)
  1212. {
  1213. m_backgroundUpdateTimer = new QTimer(this);
  1214. m_backgroundUpdateTimer->start(pBackgroundUpdatePeriod->GetIVal());
  1215. connect(m_backgroundUpdateTimer, &QTimer::timeout, this, [&]() {
  1216. // Make sure that visible editor window get low-fps updates while in the background
  1217. CCryEditApp* pApp = CCryEditApp::instance();
  1218. if (!isMinimized() && !pApp->IsWindowInForeground())
  1219. {
  1220. pApp->IdleProcessing(true);
  1221. }
  1222. });
  1223. }
  1224. }
  1225. void MainWindow::UpdateToolsMenu()
  1226. {
  1227. m_levelEditorMenuHandler->UpdateMacrosMenu();
  1228. }
  1229. int MainWindow::ViewPaneVersion() const
  1230. {
  1231. return m_levelEditorMenuHandler->GetViewPaneVersion();
  1232. }
  1233. void MainWindow::OnStopAllSounds()
  1234. {
  1235. Audio::SAudioRequest oStopAllSoundsRequest;
  1236. Audio::SAudioManagerRequestData<Audio::eAMRT_STOP_ALL_SOUNDS> oStopAllSoundsRequestData;
  1237. oStopAllSoundsRequest.pData = &oStopAllSoundsRequestData;
  1238. CryLogAlways("<Audio> Executed \"Stop All Sounds\" command.");
  1239. Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oStopAllSoundsRequest);
  1240. }
  1241. void MainWindow::OnRefreshAudioSystem()
  1242. {
  1243. QString sLevelName = GetIEditor()->GetGameEngine()->GetLevelName();
  1244. if (QString::compare(sLevelName, "Untitled", Qt::CaseInsensitive) == 0)
  1245. {
  1246. // Rather pass NULL to indicate that no level is loaded!
  1247. sLevelName = QString();
  1248. }
  1249. Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::RefreshAudioSystem, sLevelName.toUtf8().data());
  1250. }
  1251. void MainWindow::SaveLayout()
  1252. {
  1253. const int MAX_LAYOUTS = ID_VIEW_LAYOUT_LAST - ID_VIEW_LAYOUT_FIRST + 1;
  1254. if (m_viewPaneManager->LayoutNames(true).count() >= MAX_LAYOUTS)
  1255. {
  1256. QMessageBox::critical(this, tr("Maximum number of layouts reached"), tr("Please delete a saved layout before creating another."));
  1257. return;
  1258. }
  1259. QString layoutName = QInputDialog::getText(this, tr("Layout Name"), QString()).toLower();
  1260. if (layoutName.isEmpty())
  1261. {
  1262. return;
  1263. }
  1264. if (m_viewPaneManager->HasLayout(layoutName))
  1265. {
  1266. QMessageBox box(this); // Not static so we can remove help button
  1267. box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
  1268. box.setText(tr("Overwrite Layout?"));
  1269. box.setIcon(QMessageBox::Warning);
  1270. box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
  1271. box.setInformativeText(tr("The chosen layout name already exists. Do you want to overwrite it?"));
  1272. if (box.exec() != QMessageBox::Yes)
  1273. {
  1274. SaveLayout();
  1275. return;
  1276. }
  1277. }
  1278. m_viewPaneManager->SaveLayout(layoutName);
  1279. }
  1280. void MainWindow::ViewDeletePaneLayout(const QString& layoutName)
  1281. {
  1282. if (layoutName.isEmpty())
  1283. {
  1284. return;
  1285. }
  1286. QMessageBox box(this); // Not static so we can remove help button
  1287. box.setText(tr("Delete Layout?"));
  1288. box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
  1289. box.setIcon(QMessageBox::Warning);
  1290. box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
  1291. box.setInformativeText(tr("Are you sure you want to delete the layout '%1'?").arg(layoutName));
  1292. if (box.exec() == QMessageBox::Yes)
  1293. {
  1294. m_viewPaneManager->RemoveLayout(layoutName);
  1295. }
  1296. }
  1297. void MainWindow::ViewRenamePaneLayout(const QString& layoutName)
  1298. {
  1299. if (layoutName.isEmpty())
  1300. {
  1301. return;
  1302. }
  1303. QString newLayoutName;
  1304. bool validName = false;
  1305. while (!validName)
  1306. {
  1307. newLayoutName = QInputDialog::getText(this, tr("Rename layout '%1'").arg(layoutName), QString());
  1308. if (newLayoutName.isEmpty())
  1309. {
  1310. return;
  1311. }
  1312. if (m_viewPaneManager->HasLayout(newLayoutName))
  1313. {
  1314. QMessageBox box(this); // Not static so we can remove help button
  1315. box.setText(tr("Layout name already exists"));
  1316. box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
  1317. box.setIcon(QMessageBox::Warning);
  1318. box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
  1319. box.setInformativeText(tr("The layout name '%1' already exists, please choose a different name").arg(newLayoutName));
  1320. if (box.exec() == QMessageBox::No)
  1321. {
  1322. return;
  1323. }
  1324. }
  1325. else
  1326. {
  1327. validName = true;
  1328. }
  1329. }
  1330. m_viewPaneManager->RenameLayout(layoutName, newLayoutName);
  1331. }
  1332. void MainWindow::ViewLoadPaneLayout(const QString& layoutName)
  1333. {
  1334. if (!layoutName.isEmpty())
  1335. {
  1336. m_viewPaneManager->RestoreLayout(layoutName);
  1337. }
  1338. }
  1339. void MainWindow::ViewSavePaneLayout(const QString& layoutName)
  1340. {
  1341. if (layoutName.isEmpty())
  1342. {
  1343. return;
  1344. }
  1345. QMessageBox box(this); // Not static so we can remove help button
  1346. box.setText(tr("Overwrite Layout?"));
  1347. box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
  1348. box.setIcon(QMessageBox::Warning);
  1349. box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
  1350. box.setInformativeText(tr("Do you want to overwrite the layout '%1' with the current one?").arg(layoutName));
  1351. if (box.exec() == QMessageBox::Yes)
  1352. {
  1353. m_viewPaneManager->SaveLayout(layoutName);
  1354. }
  1355. }
  1356. void MainWindow::OnUpdateConnectionStatus()
  1357. {
  1358. auto* statusBar = StatusBar();
  1359. if (!m_connectionListener)
  1360. {
  1361. statusBar->SetItem("connection", tr("Disconnected"), tr("Disconnected"), IDI_BALL_DISABLED);
  1362. //TODO: disable clicking
  1363. }
  1364. else
  1365. {
  1366. using EConnectionState = EngineConnectionListener::EConnectionState;
  1367. int icon = IDI_BALL_OFFLINE;
  1368. QString tooltip, status;
  1369. switch (m_connectionListener->GetState())
  1370. {
  1371. case EConnectionState::Connecting:
  1372. // Checking whether we are not connected here instead of disconnect state because this function is called on a timer
  1373. // and therefore we may not receive the disconnect state.
  1374. if (m_connectedToAssetProcessor)
  1375. {
  1376. m_connectedToAssetProcessor = false;
  1377. m_showAPDisconnectDialog = true;
  1378. }
  1379. tooltip = tr("Connecting to Asset Processor");
  1380. icon = IDI_BALL_PENDING;
  1381. break;
  1382. case EConnectionState::Disconnecting:
  1383. tooltip = tr("Disconnecting from Asset Processor");
  1384. icon = IDI_BALL_PENDING;
  1385. break;
  1386. case EConnectionState::Listening:
  1387. if (m_connectedToAssetProcessor)
  1388. {
  1389. m_connectedToAssetProcessor = false;
  1390. m_showAPDisconnectDialog = true;
  1391. }
  1392. tooltip = tr("Listening for incoming connections");
  1393. icon = IDI_BALL_PENDING;
  1394. break;
  1395. case EConnectionState::Connected:
  1396. m_connectedToAssetProcessor = true;
  1397. tooltip = tr("Connected to Asset Processor");
  1398. icon = IDI_BALL_ONLINE;
  1399. break;
  1400. case EConnectionState::Disconnected:
  1401. icon = IDI_BALL_OFFLINE;
  1402. tooltip = tr("Disconnected from Asset Processor");
  1403. break;
  1404. }
  1405. if (m_connectedToAssetProcessor)
  1406. {
  1407. m_connectionLostTimer->stop();
  1408. }
  1409. tooltip += "\n Last Asset Processor Task: ";
  1410. tooltip += m_connectionListener->LastAssetProcessorTask().c_str();
  1411. tooltip += "\n";
  1412. AZStd::set<AZStd::string> failedJobs = m_connectionListener->FailedJobsList();
  1413. int failureCount = failedJobs.size();
  1414. if (failureCount)
  1415. {
  1416. tooltip += "\n Failed Jobs\n";
  1417. for (auto failedJob : failedJobs)
  1418. {
  1419. tooltip += failedJob.c_str();
  1420. tooltip += "\n";
  1421. }
  1422. }
  1423. status = tr("Pending Jobs : %1 Failed Jobs : %2").arg(m_connectionListener->GetJobsCount()).arg(failureCount);
  1424. statusBar->SetItem(QtUtil::ToQString("connection"), status, tooltip, icon);
  1425. if (m_showAPDisconnectDialog && m_connectionListener->GetState() != EConnectionState::Connected)
  1426. {
  1427. m_showAPDisconnectDialog = false;// Just show the dialog only once if connection is lost
  1428. m_connectionLostTimer->setSingleShot(true);
  1429. m_connectionLostTimer->start(15000);
  1430. }
  1431. }
  1432. }
  1433. void MainWindow::ShowConnectionDisconnectedDialog()
  1434. {
  1435. // when REMOTE_ASSET_PROCESSOR is undef'd it means behave as if there is no such thing as the remote asset processor.
  1436. #ifdef REMOTE_ASSET_PROCESSOR
  1437. if (gEnv && gEnv->pSystem)
  1438. {
  1439. QMessageBox messageBox(this);
  1440. messageBox.setWindowTitle(tr("Asset Processor has disconnected."));
  1441. messageBox.setText(
  1442. tr("Asset Processor is not connected. Please try (re)starting the Asset Processor or restarting the Editor.<br><br>"
  1443. "Data may be lost while the Asset Processor is not running!<br>"
  1444. "The status of the Asset Processor can be monitored from the editor in the bottom-right corner of the status bar.<br><br>"
  1445. "Would you like to start the asset processor?<br>"));
  1446. messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Ignore);
  1447. messageBox.setDefaultButton(QMessageBox::Yes);
  1448. messageBox.setIcon(QMessageBox::Critical);
  1449. if (messageBox.exec() == QMessageBox::Yes)
  1450. {
  1451. AzFramework::AssetSystem::LaunchAssetProcessor();
  1452. }
  1453. }
  1454. else
  1455. {
  1456. QMessageBox::critical(this, tr("Asset Processor has disconnected."),
  1457. tr("Asset Processor is not connected. Please try (re)starting the asset processor or restarting the Editor.<br><br>"
  1458. "Data may be lost while the asset processor is not running!<br>"
  1459. "The status of the asset processor can be monitored from the editor in the bottom-right corner of the status bar."));
  1460. }
  1461. #endif
  1462. }
  1463. void MainWindow::OnConnectionStatusClicked()
  1464. {
  1465. AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystemRequestBus::Events::ShowAssetProcessor);
  1466. }
  1467. static bool paneLessThan(const QtViewPane& v1, const QtViewPane& v2)
  1468. {
  1469. return v1.m_name.compare(v2.m_name, Qt::CaseInsensitive) < 0;
  1470. }
  1471. void MainWindow::RegisterOpenWndCommands()
  1472. {
  1473. s_openViewCmds.clear();
  1474. auto panes = m_viewPaneManager->GetRegisteredPanes(/* viewPaneMenuOnly=*/ false);
  1475. std::sort(panes.begin(), panes.end(), paneLessThan);
  1476. for (auto viewPane : panes)
  1477. {
  1478. if (viewPane.m_category.isEmpty())
  1479. {
  1480. continue;
  1481. }
  1482. const QString className = viewPane.m_name;
  1483. // Make a open-view command for the class.
  1484. QString classNameLowered = viewPane.m_name.toLower();
  1485. classNameLowered.replace(' ', '_');
  1486. QString openCommandName = "open_";
  1487. openCommandName += classNameLowered;
  1488. CEditorOpenViewCommand* pCmd = new CEditorOpenViewCommand(GetIEditor(), viewPane.m_name);
  1489. s_openViewCmds.push_back(pCmd);
  1490. CCommand0::SUIInfo cmdUI;
  1491. cmdUI.caption = className.toUtf8().data();
  1492. cmdUI.tooltip = (QString("Open ") + className).toUtf8().data();
  1493. cmdUI.iconFilename = className.toUtf8().data();
  1494. GetIEditor()->GetCommandManager()->RegisterUICommand("editor", openCommandName.toUtf8().data(),
  1495. "", "", AZStd::bind(&CEditorOpenViewCommand::Execute, pCmd), cmdUI);
  1496. GetIEditor()->GetCommandManager()->GetUIInfo("editor", openCommandName.toUtf8().data(), cmdUI);
  1497. }
  1498. }
  1499. bool MainWindow::event(QEvent* event)
  1500. {
  1501. #ifdef Q_OS_MAC
  1502. if (event->type() == QEvent::HoverMove)
  1503. {
  1504. // this fixes a problem on macOS where the mouse cursor was not
  1505. // set when hovering over the splitter handles between dock widgets
  1506. // it might be fixed in future Qt versions
  1507. auto mouse = static_cast<QHoverEvent*>(event);
  1508. bool result = QMainWindow::event(event);
  1509. void setCocoaMouseCursor(QWidget*);
  1510. setCocoaMouseCursor(childAt(mouse->pos()));
  1511. return result;
  1512. }
  1513. #endif
  1514. return QMainWindow::event(event);
  1515. }
  1516. void MainWindow::ToggleConsole()
  1517. {
  1518. m_viewPaneManager->TogglePane(LyViewPane::Console);
  1519. QtViewPane* pane = m_viewPaneManager->GetPane(LyViewPane::Console);
  1520. if (!pane)
  1521. {
  1522. return;
  1523. }
  1524. // If we toggled the console on, we want to focus its input text field
  1525. if (pane->IsVisible())
  1526. {
  1527. CConsoleSCB* console = qobject_cast<CConsoleSCB*>(pane->Widget());
  1528. if (!console)
  1529. {
  1530. return;
  1531. }
  1532. console->SetInputFocus();
  1533. }
  1534. }
  1535. void MainWindow::OnViewPaneCreated(const QtViewPane* pane)
  1536. {
  1537. QAction* action = nullptr;
  1538. int id = pane->m_id;
  1539. // Use built-in action id if available
  1540. if (pane->m_options.builtInActionId != -1)
  1541. {
  1542. id = pane->m_options.builtInActionId;
  1543. }
  1544. if (m_actionManager->HasAction(id))
  1545. {
  1546. action = m_actionManager->GetAction(id);
  1547. action->setChecked(true);
  1548. connect(pane->m_dockWidget->toggleViewAction(), &QAction::toggled,
  1549. action, &QAction::setChecked, Qt::UniqueConnection);
  1550. }
  1551. }
  1552. void MainWindow::ConnectivityStateChanged(const AzToolsFramework::SourceControlState state)
  1553. {
  1554. bool connected = false;
  1555. ISourceControl* pSourceControl = GetIEditor() ? GetIEditor()->GetSourceControl() : nullptr;
  1556. if (pSourceControl)
  1557. {
  1558. pSourceControl->SetSourceControlState(state);
  1559. if (state == AzToolsFramework::SourceControlState::Active || state == AzToolsFramework::SourceControlState::ConfigurationInvalid)
  1560. {
  1561. connected = true;
  1562. }
  1563. }
  1564. gSettings.enableSourceControl = connected;
  1565. gSettings.SaveEnableSourceControlFlag(false);
  1566. }
  1567. void MainWindow::OnGotoSelected()
  1568. {
  1569. AzToolsFramework::EditorRequestBus::Broadcast(&AzToolsFramework::EditorRequestBus::Events::GoToSelectedEntitiesInViewports);
  1570. }
  1571. void MainWindow::OnGotoSliceRoot()
  1572. {
  1573. int numViews = GetIEditor()->GetViewManager()->GetViewCount();
  1574. for (int i = 0; i < numViews; ++i)
  1575. {
  1576. CViewport* viewport = GetIEditor()->GetViewManager()->GetView(i);
  1577. if (viewport)
  1578. {
  1579. viewport->CenterOnSliceInstance();
  1580. }
  1581. }
  1582. }
  1583. void MainWindow::ShowCustomizeToolbarDialog()
  1584. {
  1585. if (m_toolbarCustomizationDialog)
  1586. {
  1587. return;
  1588. }
  1589. m_toolbarCustomizationDialog = new ToolbarCustomizationDialog(this);
  1590. m_toolbarCustomizationDialog->show();
  1591. }
  1592. QMenu* MainWindow::createPopupMenu()
  1593. {
  1594. QMenu* menu = QMainWindow::createPopupMenu();
  1595. menu->addSeparator();
  1596. QAction* action = menu->addAction(QStringLiteral("Customize..."));
  1597. connect(action, &QAction::triggered, this, &MainWindow::ShowCustomizeToolbarDialog);
  1598. return menu;
  1599. }
  1600. ToolbarManager* MainWindow::GetToolbarManager() const
  1601. {
  1602. return m_toolbarManager;
  1603. }
  1604. bool MainWindow::IsCustomizingToolbars() const
  1605. {
  1606. return m_toolbarCustomizationDialog != nullptr;
  1607. }
  1608. QWidget* MainWindow::CreateToolbarWidget(int actionId)
  1609. {
  1610. QWidgetAction* action = qobject_cast<QWidgetAction*>(m_actionManager->GetAction(actionId));
  1611. if (!action)
  1612. {
  1613. qWarning() << Q_FUNC_INFO << "No QWidgetAction for actionId = " << actionId;
  1614. return nullptr;
  1615. }
  1616. QWidget* w = nullptr;
  1617. switch (actionId)
  1618. {
  1619. case ID_TOOLBAR_WIDGET_UNDO:
  1620. w = CreateUndoRedoButton(ID_UNDO);
  1621. break;
  1622. case ID_TOOLBAR_WIDGET_REDO:
  1623. w = CreateUndoRedoButton(ID_REDO);
  1624. break;
  1625. case ID_TOOLBAR_WIDGET_SPACER_RIGHT:
  1626. w = CreateSpacerRightWidget();
  1627. break;
  1628. default:
  1629. qWarning() << Q_FUNC_INFO << "Unknown id " << actionId;
  1630. return nullptr;
  1631. }
  1632. return w;
  1633. }
  1634. // 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
  1635. // and are reading it as an event rather.
  1636. void MainWindow::keyPressEvent(QKeyEvent* e)
  1637. {
  1638. // We shouldn't need to do this, as there's already an escape key shortcut set on an action
  1639. // attached to the MainWindow. We need to explicitly trap the escape key here because when in
  1640. // Game Mode, all of the actions attached to the MainWindow are disabled.
  1641. if (e->key() == Qt::Key_Escape)
  1642. {
  1643. MainWindow::OnEscapeAction();
  1644. return;
  1645. }
  1646. return QMainWindow::keyPressEvent(e);
  1647. }
  1648. void MainWindow::dragEnterEvent(QDragEnterEvent *event)
  1649. {
  1650. using namespace AzQtComponents;
  1651. DragAndDropContextBase context;
  1652. DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::DragEnter, event, context);
  1653. }
  1654. void MainWindow::dragMoveEvent(QDragMoveEvent* event)
  1655. {
  1656. using namespace AzQtComponents;
  1657. DragAndDropContextBase context;
  1658. DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::DragMove, event, context);
  1659. }
  1660. void MainWindow::dragLeaveEvent(QDragLeaveEvent* event)
  1661. {
  1662. using namespace AzQtComponents;
  1663. DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::DragLeave, event);
  1664. }
  1665. void MainWindow::dropEvent(QDropEvent *event)
  1666. {
  1667. using namespace AzQtComponents;
  1668. DragAndDropContextBase context;
  1669. DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::Drop, event, context);
  1670. }
  1671. bool MainWindow::focusNextPrevChild(bool next)
  1672. {
  1673. // Don't change the focus when we're in game mode or else the viewport could
  1674. // stop receiving input events
  1675. if (GetIEditor()->IsInGameMode())
  1676. {
  1677. return false;
  1678. }
  1679. return QMainWindow::focusNextPrevChild(next);
  1680. }
  1681. namespace AzToolsFramework
  1682. {
  1683. void MainWindowEditorFuncsHandler::Reflect(AZ::ReflectContext* context)
  1684. {
  1685. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  1686. {
  1687. // this will put these methods into the 'azlmbr.legacy.general' module
  1688. auto addLegacyGeneral = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
  1689. {
  1690. methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  1691. ->Attribute(AZ::Script::Attributes::Category, "Legacy/Editor")
  1692. ->Attribute(AZ::Script::Attributes::Module, "legacy.general");
  1693. };
  1694. addLegacyGeneral(behaviorContext->Method("open_pane", PyOpenViewPane, nullptr, "Opens a view pane specified by the pane class name."));
  1695. addLegacyGeneral(behaviorContext->Method("close_pane", PyCloseViewPane, nullptr, "Closes a view pane specified by the pane class name."));
  1696. addLegacyGeneral(behaviorContext->Method("is_pane_visible", PyIsViewPaneVisible, nullptr, "Returns true if pane specified by the pane class name is visible."));
  1697. addLegacyGeneral(behaviorContext->Method("get_pane_class_names", PyGetViewPaneNames, nullptr, "Get all available class names for use with open_pane & close_pane."));
  1698. addLegacyGeneral(behaviorContext->Method("exit", PyExit, nullptr, "Exits the editor."));
  1699. addLegacyGeneral(behaviorContext->Method("exit_no_prompt", PyExitNoPrompt, nullptr, "Exits the editor without prompting to save first."));
  1700. addLegacyGeneral(behaviorContext->Method("test_output", PyTestOutput, nullptr, "Report test information."));
  1701. }
  1702. }
  1703. }
  1704. #include <moc_MainWindow.cpp>