MainWindow.cpp 40 KB

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