12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "EditorDefs.h"
- #include "MainWindow.h"
- #include <algorithm>
- // Qt
- #ifdef Q_OS_WIN
- #include <QAbstractEventDispatcher>
- #endif
- #include <QDebug>
- #include <QHBoxLayout>
- #include <QInputDialog>
- #include <QMenuBar>
- #include <QMessageBox>
- #include <QToolBar>
- // AzCore
- #include <AzCore/Component/ComponentApplication.h>
- #include <AzCore/Interface/Interface.h>
- #include <AzCore/RTTI/BehaviorContext.h>
- #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
- #include <AzCore/std/smart_ptr/make_shared.h>
- #include <AzCore/Utils/Utils.h>
- // AzFramework
- #include <AzFramework/API/ApplicationAPI.h>
- #include <AzFramework/Asset/AssetSystemBus.h>
- #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
- #include <AzFramework/Network/SocketConnection.h>
- #include <AzFramework/Asset/AssetSystemComponent.h>
- #include <AzFramework/Viewport/CameraInput.h>
- // AzToolsFramework
- #include <AzToolsFramework/API/EditorCameraBus.h>
- #include <AzToolsFramework/Application/Ticker.h>
- #include <AzToolsFramework/API/EditorWindowRequestBus.h>
- #include <AzToolsFramework/API/EditorAnimationSystemRequestBus.h>
- #include <AzToolsFramework/Editor/ActionManagerUtils.h>
- #include <AzToolsFramework/PaintBrush/GlobalPaintBrushSettingsWindow.h>
- #include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
- #include <AzToolsFramework/SourceControl/QtSourceControlNotificationHandler.h>
- #include <AzToolsFramework/Viewport/LocalViewBookmarkLoader.h>
- #include <AzToolsFramework/Viewport/ViewportSettings.h>
- #include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
- // AzQtComponents
- #include <AzQtComponents/Buses/ShortcutDispatch.h>
- #include <AzQtComponents/Components/DockMainWindow.h>
- #include <AzQtComponents/Components/InputDialog.h>
- #include <AzQtComponents/Components/Style.h>
- #include <AzQtComponents/Components/Widgets/SpinBox.h>
- #include <AzQtComponents/Components/WindowDecorationWrapper.h>
- #include <AzQtComponents/DragAndDrop/MainWindowDragAndDrop.h>
- // Editor
- #include "Resource.h"
- #include "LayoutWnd.h"
- #include "AssetImporter/AssetImporterManager/AssetImporterManager.h"
- #include "AssetImporter/AssetImporterManager/AssetImporterDragAndDropHandler.h"
- #include "CryEdit.h"
- #include "Controls/ConsoleSCB.h"
- #include "ViewManager.h"
- #include "CryEditDoc.h"
- #include "ToolBox.h"
- #include "LevelIndependentFileMan.h"
- #include "GameEngine.h"
- #include "MainStatusBar.h"
- #include "Core/QtEditorApplication.h"
- #include "UndoDropDown.h"
- #include "EditorViewportSettings.h"
- #include "QtViewPaneManager.h"
- #include "ViewPane.h"
- #include "Include/IObjectManager.h"
- #include "Include/Command.h"
- #include "Commands/CommandManager.h"
- #include "SettingsManagerDialog.h"
- #include "TrackView/TrackViewDialog.h"
- #include "ErrorReportDialog.h"
- #include "Dialogs/PythonScriptsDialog.h"
- #include "AzAssetBrowser/AzAssetBrowserWindow.h"
- #include "AssetEditor/AssetEditorWindow.h"
- #include <ImGuiBus.h>
- #include <AzToolsFramework/Viewport/ViewportMessages.h>
- #include <LmbrCentral/Audio/AudioSystemComponentBus.h>
- #include <Editor/EditorViewportCamera.h>
- using namespace AZ;
- using namespace AzQtComponents;
- using namespace AzToolsFramework;
- #define LAYOUTS_PATH "Editor\\Layouts\\"
- #define LAYOUTS_EXTENSION ".layout"
- #define LAYOUTS_WILDCARD "*.layout"
- #define DUMMY_LAYOUT_NAME "Dummy_Layout"
- class CEditorOpenViewCommand
- : public _i_reference_target_t
- {
- QString m_className;
- public:
- CEditorOpenViewCommand(IEditor* pEditor, const QString& className)
- : m_pEditor(pEditor)
- , m_className(className)
- {
- assert(m_pEditor);
- }
- void Execute()
- {
- // Create browse mode for this category.
- m_pEditor->OpenView(m_className);
- }
- private:
- IEditor* m_pEditor;
- };
- namespace
- {
- // The purpose of this vector is just holding shared pointers, so CEditorOpenViewCommand dtors are called at exit
- std::vector<_smart_ptr<CEditorOpenViewCommand> > s_openViewCmds;
- }
- class EngineConnectionListener
- : public AzFramework::EngineConnectionEvents::Bus::Handler
- , public AzFramework::AssetSystemInfoBus::Handler
- {
- public:
- using EConnectionState = AzFramework::SocketConnection::EConnectionState;
- public:
- EngineConnectionListener()
- : m_state(EConnectionState::Disconnected)
- {
- AzFramework::EngineConnectionEvents::Bus::Handler::BusConnect();
- AzFramework::AssetSystemInfoBus::Handler::BusConnect();
- AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
- if (engineConnection)
- {
- m_state = engineConnection->GetConnectionState();
- }
- }
- ~EngineConnectionListener() override
- {
- AzFramework::AssetSystemInfoBus::Handler::BusDisconnect();
- AzFramework::EngineConnectionEvents::Bus::Handler::BusDisconnect();
- }
- public:
- void Connected([[maybe_unused]] AzFramework::SocketConnection* connection) override
- {
- m_state = EConnectionState::Connected;
- }
- void Connecting([[maybe_unused]] AzFramework::SocketConnection* connection) override
- {
- m_state = EConnectionState::Connecting;
- }
- void Listening([[maybe_unused]] AzFramework::SocketConnection* connection) override
- {
- m_state = EConnectionState::Listening;
- }
- void Disconnecting([[maybe_unused]] AzFramework::SocketConnection* connection) override
- {
- m_state = EConnectionState::Disconnecting;
- }
- void Disconnected([[maybe_unused]] AzFramework::SocketConnection* connection) override
- {
- m_state = EConnectionState::Disconnected;
- }
- void AssetCompilationSuccess(const AZStd::string& assetPath) override
- {
- m_lastAssetProcessorTask = assetPath;
- }
- void AssetCompilationFailed(const AZStd::string& assetPath) override
- {
- m_failedJobs.insert(assetPath);
- }
- void CountOfAssetsInQueue(const int& count) override
- {
- m_pendingJobsCount = count;
- }
- int GetJobsCount() const
- {
- return m_pendingJobsCount;
- }
- AZStd::set<AZStd::string> FailedJobsList() const
- {
- return m_failedJobs;
- }
- AZStd::string LastAssetProcessorTask() const
- {
- return m_lastAssetProcessorTask;
- }
- public:
- EConnectionState GetState() const
- {
- return m_state;
- }
- private:
- EConnectionState m_state;
- int m_pendingJobsCount = 0;
- AZStd::set<AZStd::string> m_failedJobs;
- AZStd::string m_lastAssetProcessorTask;
- };
- namespace
- {
- void PyOpenViewPane(const char* viewClassName)
- {
- QtViewPaneManager::instance()->OpenPane(viewClassName);
- }
- void PyCloseViewPane(const char* viewClassName)
- {
- QtViewPaneManager::instance()->ClosePane(viewClassName);
- }
- bool PyIsViewPaneVisible(const char* viewClassName)
- {
- return QtViewPaneManager::instance()->IsVisible(viewClassName);
- }
- AZStd::vector<AZStd::string> PyGetViewPaneNames()
- {
- const QtViewPanes panes = QtViewPaneManager::instance()->GetRegisteredPanes();
- AZStd::vector<AZStd::string> names;
- names.reserve(panes.size());
- AZStd::transform(panes.begin(), panes.end(), AZStd::back_inserter(names), [](const QtViewPane& pane)
- {
- return AZStd::string(pane.m_name.toUtf8().constData());
- });
- return names;
- }
- void PyExit()
- {
- // Adding a single-shot QTimer to PyExit delays the QApplication::closeAllWindows call until
- // all the events in the event queue have been processed. Calling QApplication::closeAllWindows instead
- // of MainWindow::close ensures the Metal render window is cleaned up on macOS.
- QTimer::singleShot(0, qApp, &QApplication::closeAllWindows);
- }
- void PyExitNoPrompt()
- {
- // Set the level to "unmodified" so that it doesn't prompt to save on exit.
- GetIEditor()->GetDocument()->SetModifiedFlag(false);
- PyExit();
- }
- void PyTestOutput(const AZStd::string& output)
- {
- CCryEditApp::instance()->PrintAlways(output);
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- // MainWindow
- /////////////////////////////////////////////////////////////////////////////
- MainWindow* MainWindow::m_instance = nullptr;
- MainWindow::MainWindow(QWidget* parent)
- : QMainWindow(parent)
- , m_oldMainFrame(nullptr)
- , m_viewPaneManager(QtViewPaneManager::instance())
- , m_undoStateAdapter(new UndoStackStateAdapter(this))
- , m_activeView(nullptr)
- , m_settings("O3DE", "O3DE")
- , m_assetImporterManager(new AssetImporterManager(this))
- , m_sourceControlNotifHandler(new AzToolsFramework::QtSourceControlNotificationHandler(this))
- , m_viewPaneHost(nullptr)
- , m_autoSaveTimer(nullptr)
- , m_autoRemindTimer(nullptr)
- , m_backgroundUpdateTimer(nullptr)
- , m_connectionLostTimer(new QTimer(this))
- {
- setObjectName("MainWindow"); // For IEditor::GetEditorMainWindow to work in plugins, where we can't link against MainWindow::instance()
- m_instance = this;
- //for new docking, create a DockMainWindow to host dock widgets so we can call QMainWindow::restoreState to restore docks without affecting our main toolbars.
- m_viewPaneHost = new AzQtComponents::DockMainWindow();
- m_viewPaneHost->setDockOptions(QMainWindow::GroupedDragging | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
- m_connectionListener = AZStd::make_shared<EngineConnectionListener>();
- QObject::connect(m_connectionLostTimer, &QTimer::timeout, this, &MainWindow::ShowConnectionDisconnectedDialog);
- setStatusBar(new MainStatusBar(this));
- setAttribute(Qt::WA_DeleteOnClose, true);
- GetIEditor()->RegisterNotifyListener(this);
- AssetImporterDragAndDropHandler* assetImporterDragAndDropHandler = new AssetImporterDragAndDropHandler(this, m_assetImporterManager);
- connect(assetImporterDragAndDropHandler, &AssetImporterDragAndDropHandler::OpenAssetImporterManager, this, &MainWindow::OnOpenAssetImporterManager);
- connect(assetImporterDragAndDropHandler, &AssetImporterDragAndDropHandler::OpenAssetImporterManagerWithSuggestedPath, this, &MainWindow::OnOpenAssetImporterManagerAtPath);
-
- setFocusPolicy(Qt::StrongFocus);
- setAcceptDrops(true);
- // special handling for escape key (outside ActionManager)
- auto* escapeAction = new QAction(this);
- escapeAction->setShortcut(QKeySequence(Qt::Key_Escape));
- addAction(escapeAction);
- connect(escapeAction, &QAction::triggered, this, &MainWindow::OnEscapeAction);
- const QSize minSize(800, 600);
- if (size().height() < minSize.height() || size().width() < minSize.width())
- {
- resize(size().expandedTo(minSize));
- }
- }
- void MainWindow::SystemTick()
- {
- AZ::ComponentApplication* componentApplication = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(componentApplication, &AZ::ComponentApplicationBus::Events::GetApplication);
- if (componentApplication)
- {
- componentApplication->TickSystem();
- }
- }
- #ifdef Q_OS_WIN
- HWND MainWindow::GetNativeHandle()
- {
- // if the parent widget is set, it's a window decoration wrapper
- // we use that instead, to ensure we're in lock step the code in CryEdit.cpp when it calls
- // InitGameSystem
- if (parentWidget() != nullptr)
- {
- assert(qobject_cast<AzQtComponents::WindowDecorationWrapper*>(parentWidget()));
- return QtUtil::getNativeHandle(parentWidget());
- }
- return QtUtil::getNativeHandle(this);
- }
- #endif // #ifdef Q_OS_WIN
- void MainWindow::OnOpenAssetImporterManager(const QStringList& dragAndDropFileList)
- {
- m_assetImporterManager->Exec(dragAndDropFileList);
- }
- void MainWindow::OnOpenAssetImporterManagerAtPath(const QStringList& dragAndDropFileList, const QString& path)
- {
- m_assetImporterManager->Exec(dragAndDropFileList, path);
- }
- CLayoutWnd* MainWindow::GetLayout() const
- {
- return m_pLayoutWnd;
- }
- CLayoutViewPane* MainWindow::GetActiveView() const
- {
- return m_activeView;
- }
- QtViewport* MainWindow::GetActiveViewport() const
- {
- return m_activeView ? qobject_cast<QtViewport*>(m_activeView->GetViewport()) : nullptr;
- }
- void MainWindow::SetActiveView(CLayoutViewPane* v)
- {
- m_activeView = v;
- }
- MainWindow::~MainWindow()
- {
- AzToolsFramework::SourceControlNotificationBus::Handler::BusDisconnect();
- m_connectionListener.reset();
- GetIEditor()->UnregisterNotifyListener(this);
- m_instance = nullptr;
- }
- void MainWindow::InitCentralWidget()
- {
- m_pLayoutWnd = new CLayoutWnd(&m_settings);
- // Set the central widgets before calling CreateLayout to avoid reparenting everything later
- setCentralWidget(m_viewPaneHost);
- m_viewPaneHost->setCentralWidget(m_pLayoutWnd);
- if (MainWindow::instance()->IsPreview())
- {
- m_pLayoutWnd->CreateLayout(ET_Layout0, true, ET_ViewportModel);
- }
- else
- {
- if (!m_pLayoutWnd->LoadConfig())
- {
- m_pLayoutWnd->CreateLayout(ET_Layout0);
- }
- }
- // make sure the layout wnd knows to reset it's layout and settings
- connect(m_viewPaneManager, &QtViewPaneManager::layoutReset, m_pLayoutWnd, &CLayoutWnd::ResetLayout);
- AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::Bus::Events::NotifyCentralWidgetInitialized);
- }
- void MainWindow::Initialize()
- {
- m_viewPaneManager->SetMainWindow(m_viewPaneHost, &m_settings, /*unused*/ QByteArray());
- RegisterStdViewClasses();
- InitCentralWidget();
- // load toolbars ("shelves") and macros
- GetIEditor()->GetToolBoxManager()->Load();
- m_editorActionsHandler.Initialize(this);
- InitStatusBar();
- AzToolsFramework::SourceControlNotificationBus::Handler::BusConnect();
- m_sourceControlNotifHandler->Init();
- if (!IsPreview())
- {
- RegisterOpenWndCommands();
- }
- ResetBackgroundUpdateTimer();
- ICVar* pBackgroundUpdatePeriod = gEnv->pConsole->GetCVar("ed_backgroundUpdatePeriod");
- if (pBackgroundUpdatePeriod)
- {
- pBackgroundUpdatePeriod->SetOnChangeCallback([](ICVar*) {
- MainWindow::instance()->ResetBackgroundUpdateTimer();
- });
- }
- AzToolsFramework::EditorEventsBus::Broadcast(&AzToolsFramework::EditorEvents::NotifyMainWindowInitialized, this);
- }
- void MainWindow::InitStatusBar()
- {
- StatusBar()->Init();
- connect(qobject_cast<StatusBarItem*>(StatusBar()->GetItem("connection")), &StatusBarItem::clicked, this, &MainWindow::OnConnectionStatusClicked);
- connect(StatusBar(), &MainStatusBar::requestStatusUpdate, this, &MainWindow::OnUpdateConnectionStatus);
- }
- CMainFrame* MainWindow::GetOldMainFrame() const
- {
- return m_oldMainFrame;
- }
- MainWindow* MainWindow::instance()
- {
- return m_instance;
- }
- void MainWindow::closeEvent(QCloseEvent* event)
- {
- gSettings.Save(true);
- AzFramework::SystemCursorState currentCursorState;
- bool isInGameMode = false;
- if (GetIEditor()->IsInGameMode())
- {
- isInGameMode = true;
- // Storecurrent state in case we need to restore Game Mode.
- AzFramework::InputSystemCursorRequestBus::EventResult(currentCursorState, AzFramework::InputDeviceMouse::Id,
- &AzFramework::InputSystemCursorRequests::GetSystemCursorState);
- // make sure the mouse is turned on before popping up any dialog boxes.
- AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
- &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
- AzFramework::SystemCursorState::UnconstrainedAndVisible);
- }
- if (GetIEditor()->GetDocument() && !GetIEditor()->GetDocument()->CanCloseFrame())
- {
- if (isInGameMode)
- {
- // make sure the mouse is turned back off if returning to the game.
- AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
- &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
- currentCursorState);
- }
- event->ignore();
- return;
- }
- SaveConfig();
- // Some of the panes may ask for confirmation to save changes before closing.
- if (!QtViewPaneManager::instance()->ClosePanesWithRollback(QVector<QString>()) ||
- !GetIEditor() ||
- !GetIEditor()->GetLevelIndependentFileMan()->PromptChangedFiles())
- {
- if (isInGameMode)
- {
- // make sure the mouse is turned back off if returning to the game.
- AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
- &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
- currentCursorState);
- }
- event->ignore();
- return;
- }
- Editor::EditorQtApplication::instance()->EnableOnIdle(false);
- if (GetIEditor()->GetDocument())
- {
- GetIEditor()->GetDocument()->SetModifiedFlag(false);
- GetIEditor()->GetDocument()->SetModifiedModules(eModifiedNothing);
- }
- // Close all edit panels.
- GetIEditor()->ClearSelection();
- // force clean up of all deferred deletes, so that we don't have any issues with windows from plugins not being deleted yet
- qApp->sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QMainWindow::closeEvent(event);
- }
- void MainWindow::SaveConfig()
- {
- m_settings.setValue("mainWindowState", saveState());
- QtViewPaneManager::instance()->SaveLayout();
- if (m_pLayoutWnd)
- {
- m_pLayoutWnd->SaveConfig();
- }
- }
- void MainWindow::OnEscapeAction()
- {
- if (!CCryEditApp::instance()->IsInAutotestMode())
- {
- if (GetIEditor()->IsInGameMode())
- {
- GetIEditor()->SetInGameMode(false);
- }
- else
- {
- AzToolsFramework::EditorEvents::Bus::Broadcast(
- &AzToolsFramework::EditorEvents::OnEscape);
- }
- }
- }
- QWidget* MainWindow::CreateSpacerRightWidget()
- {
- QWidget* spacer = new QWidget(this);
- spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
- spacer->setVisible(true);
- return spacer;
- }
- UndoRedoToolButton::UndoRedoToolButton(QWidget* parent)
- : QToolButton(parent)
- {
- }
- void UndoRedoToolButton::Update(int count)
- {
- setEnabled(count > 0);
- }
- bool MainWindow::IsPreview() const
- {
- return GetIEditor()->IsInPreviewMode();
- }
- MainStatusBar* MainWindow::StatusBar() const
- {
- assert(statusBar()->inherits("MainStatusBar"));
- return static_cast<MainStatusBar*>(statusBar());
- }
- void MainWindow::OpenViewPane(int paneId)
- {
- OpenViewPane(QtViewPaneManager::instance()->GetPane(paneId));
- }
- void MainWindow::OpenViewPane(QtViewPane* pane)
- {
- if (pane && pane->IsValid())
- {
- QtViewPaneManager::instance()->OpenPane(pane->m_name);
- }
- else
- {
- if (pane)
- {
- qWarning() << Q_FUNC_INFO << "Invalid pane" << pane->m_id << pane->m_category << pane->m_name;
- }
- else
- {
- qWarning() << Q_FUNC_INFO << "Invalid pane";
- }
- }
- }
- void MainWindow::AdjustToolBarIconSize(AzQtComponents::ToolBar::ToolBarIconSize size)
- {
- const QList<QToolBar*> toolbars = findChildren<QToolBar*>();
- // make sure to set this back, so that the general settings page matches up with what the size is too
- if (gSettings.gui.nToolbarIconSize != static_cast<int>(size))
- {
- gSettings.gui.nToolbarIconSize = static_cast<int>(size);
- }
- for (auto toolbar : toolbars)
- {
- AzQtComponents::ToolBar::setToolBarIconSize(toolbar, size);
- }
- }
- void MainWindow::OnEditorNotifyEvent(EEditorNotifyEvent ev)
- {
- switch (ev)
- {
- case eNotify_OnEndSceneOpen:
- case eNotify_OnEndSceneSave:
- {
- auto cryEdit = CCryEditApp::instance();
- if (cryEdit)
- {
- cryEdit->SetEditorWindowTitle(nullptr, AZ::Utils::GetProjectDisplayName().c_str(), GetIEditor()->GetGameEngine()->GetLevelName());
- }
- }
- break;
- case eNotify_OnCloseScene:
- {
- auto cryEdit = CCryEditApp::instance();
- if (cryEdit)
- {
- cryEdit->SetEditorWindowTitle(nullptr, AZ::Utils::GetProjectDisplayName().c_str(), nullptr);
- }
- }
- break;
- case eNotify_OnRefCoordSysChange:
- emit UpdateRefCoordSys();
- break;
- case eNotify_OnInvalidateControls:
- InvalidateControls();
- break;
- }
- switch (ev)
- {
- case eNotify_OnBeginSceneOpen:
- case eNotify_OnBeginNewScene:
- case eNotify_OnCloseScene:
- StopAutoSaveTimers();
- break;
- case eNotify_OnEndSceneOpen:
- case eNotify_OnEndNewScene:
- StartAutoSaveTimers();
- break;
- }
- }
- void MainWindow::InvalidateControls()
- {
- emit UpdateRefCoordSys();
- }
- void MainWindow::RegisterStdViewClasses()
- {
- AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(this);
- CTrackViewDialog::RegisterViewClass();
- CErrorReportDialog::RegisterViewClass();
- CPythonScriptsDialog::RegisterViewClass();
- AzToolsFramework::CScriptTermDialog::RegisterViewClass();
- CConsoleSCB::RegisterViewClass();
- ConsoleVariableEditor::RegisterViewClass();
- CSettingsManagerDialog::RegisterViewClass();
- AzAssetBrowserWindow::RegisterViewClass();
- AssetEditorWindow::RegisterViewClass();
- AzToolsFramework::RegisterPaintBrushSettingsWindow();
- // Notify that views can now be registered
- AzToolsFramework::EditorEvents::Bus::Broadcast(
- &AzToolsFramework::EditorEvents::Bus::Events::NotifyRegisterViews);
- }
- void MainWindow::OnCustomizeToolbar()
- {
- /* TODO_KDAB, rest of CMainFrm::OnCustomize() goes here*/
- SaveConfig();
- }
- void MainWindow::RefreshStyle()
- {
- GetIEditor()->Notify(eNotify_OnStyleChanged);
- }
- void MainWindow::StopAutoSaveTimers()
- {
- if (m_autoSaveTimer)
- {
- delete m_autoSaveTimer;
- }
- if (m_autoRemindTimer)
- {
- delete m_autoRemindTimer;
- }
- m_autoSaveTimer = nullptr;
- m_autoRemindTimer = nullptr;
- }
- void MainWindow::StartAutoSaveTimers()
- {
- if (gSettings.autoBackupTime > 0 && gSettings.autoBackupEnabled)
- {
- m_autoSaveTimer = new QTimer(this);
- m_autoSaveTimer->start(gSettings.autoBackupTime * 1000 * 60);
- connect(
- m_autoSaveTimer,
- &QTimer::timeout,
- this,
- [&]()
- {
- if (gSettings.autoBackupEnabled)
- {
- // Call autosave function of CryEditApp
- GetIEditor()->GetDocument()->SaveAutoBackup();
- }
- });
- }
- if (gSettings.autoRemindTime > 0)
- {
- m_autoRemindTimer = new QTimer(this);
- m_autoRemindTimer->start(gSettings.autoRemindTime * 1000 * 60);
- connect(
- m_autoRemindTimer,
- &QTimer::timeout,
- this,
- [&]()
- {
- if (gSettings.autoRemindTime > 0)
- {
- // Remind to save.
- CCryEditApp::instance()->SaveAutoRemind();
- }
- });
- }
- }
- void MainWindow::ResetAutoSaveTimers()
- {
- StopAutoSaveTimers();
- StartAutoSaveTimers();
- }
- void MainWindow::ResetBackgroundUpdateTimer()
- {
- if (m_backgroundUpdateTimer)
- {
- delete m_backgroundUpdateTimer;
- m_backgroundUpdateTimer = nullptr;
- }
- if (gSettings.backgroundUpdatePeriod > 0)
- {
- m_backgroundUpdateTimer = new QTimer(this);
- m_backgroundUpdateTimer->start(gSettings.backgroundUpdatePeriod);
- connect(m_backgroundUpdateTimer, &QTimer::timeout, this, [&]() {
- // Make sure that visible editor window get low-fps updates while in the background
- CCryEditApp* pApp = CCryEditApp::instance();
- if (!isMinimized() && !pApp->IsWindowInForeground())
- {
- pApp->IdleProcessing(true);
- }
- });
- }
- }
- void MainWindow::OnStopAllSounds()
- {
- LmbrCentral::AudioSystemComponentRequestBus::Broadcast(&LmbrCentral::AudioSystemComponentRequestBus::Events::GlobalStopAllSounds);
- }
- void MainWindow::OnRefreshAudioSystem()
- {
- AZStd::string levelName;
- AzToolsFramework::EditorRequestBus::BroadcastResult(levelName, &AzToolsFramework::EditorRequestBus::Events::GetLevelName);
- AZStd::to_lower(levelName.begin(), levelName.end());
- if (levelName == "untitled")
- {
- levelName.clear();
- }
- LmbrCentral::AudioSystemComponentRequestBus::Broadcast(
- &LmbrCentral::AudioSystemComponentRequestBus::Events::GlobalRefreshAudio, AZStd::string_view{ levelName });
- }
- void MainWindow::SaveLayout()
- {
- const int MAX_LAYOUTS = ID_VIEW_LAYOUT_LAST - ID_VIEW_LAYOUT_FIRST + 1;
- if (m_viewPaneManager->LayoutNames(true).count() >= MAX_LAYOUTS)
- {
- QMessageBox::critical(this, tr("Maximum number of layouts reached"), tr("Please delete a saved layout before creating another."));
- return;
- }
- QString layoutName = InputDialog::getText(this, tr("Layout Name"), QString(), QLineEdit::Normal, QString(), "[a-z]+[a-z0-9\\-\\_]*");
- if (layoutName.isEmpty())
- {
- return;
- }
- if (m_viewPaneManager->HasLayout(layoutName))
- {
- QMessageBox box(this); // Not static so we can remove help button
- box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
- box.setText(tr("Overwrite Layout?"));
- box.setIcon(QMessageBox::Warning);
- box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
- box.setInformativeText(tr("The chosen layout name already exists. Do you want to overwrite it?"));
- if (box.exec() != QMessageBox::Yes)
- {
- SaveLayout();
- return;
- }
- }
- m_viewPaneManager->SaveLayout(layoutName);
- }
- void MainWindow::ViewDeletePaneLayout(const QString& layoutName)
- {
- if (layoutName.isEmpty())
- {
- return;
- }
- QMessageBox box(this); // Not static so we can remove help button
- box.setText(tr("Delete Layout?"));
- box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
- box.setIcon(QMessageBox::Warning);
- box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
- box.setInformativeText(tr("Are you sure you want to delete the layout '%1'?").arg(layoutName));
- if (box.exec() == QMessageBox::Yes)
- {
- m_viewPaneManager->RemoveLayout(layoutName);
- }
- }
- void MainWindow::ViewRenamePaneLayout(const QString& layoutName)
- {
- if (layoutName.isEmpty())
- {
- return;
- }
- QString newLayoutName;
- bool validName = false;
- while (!validName)
- {
- newLayoutName = InputDialog::getText(this, tr("Layout Name"), QString(), QLineEdit::Normal, QString(), "[a-z]+[a-z0-9\\-\\_]*");
- if (m_viewPaneManager->HasLayout(newLayoutName))
- {
- QMessageBox box(this); // Not static so we can remove help button
- box.setText(tr("Layout name already exists"));
- box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
- box.setIcon(QMessageBox::Warning);
- box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
- box.setInformativeText(tr("The layout name '%1' already exists, please choose a different name").arg(newLayoutName));
- if (box.exec() == QMessageBox::No)
- {
- return;
- }
- }
- else
- {
- validName = true;
- }
- }
- m_viewPaneManager->RenameLayout(layoutName, newLayoutName);
- }
- void MainWindow::ViewLoadPaneLayout(const QString& layoutName)
- {
- if (!layoutName.isEmpty())
- {
- m_viewPaneManager->RestoreLayout(layoutName);
- }
- }
- void MainWindow::ViewSavePaneLayout(const QString& layoutName)
- {
- if (layoutName.isEmpty())
- {
- return;
- }
- QMessageBox box(this); // Not static so we can remove help button
- box.setText(tr("Overwrite Layout?"));
- box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
- box.setIcon(QMessageBox::Warning);
- box.setWindowFlags(box.windowFlags() & ~Qt::WindowContextHelpButtonHint);
- box.setInformativeText(tr("Do you want to overwrite the layout '%1' with the current one?").arg(layoutName));
- if (box.exec() == QMessageBox::Yes)
- {
- m_viewPaneManager->SaveLayout(layoutName);
- }
- }
- void MainWindow::OnUpdateConnectionStatus()
- {
- auto* statusBar = StatusBar();
- if (!m_connectionListener)
- {
- statusBar->SetItem("connection", tr("Disconnected"), tr("Disconnected"), IDI_BALL_DISABLED);
- //TODO: disable clicking
- }
- else
- {
- using EConnectionState = EngineConnectionListener::EConnectionState;
- int icon = IDI_BALL_OFFLINE;
- QString tooltip, status;
- switch (m_connectionListener->GetState())
- {
- case EConnectionState::Connecting:
- // Checking whether we are not connected here instead of disconnect state because this function is called on a timer
- // and therefore we may not receive the disconnect state.
- if (m_connectedToAssetProcessor)
- {
- m_connectedToAssetProcessor = false;
- m_showAPDisconnectDialog = true;
- }
- tooltip = tr("Connecting to Asset Processor");
- icon = IDI_BALL_PENDING;
- break;
- case EConnectionState::Disconnecting:
- tooltip = tr("Disconnecting from Asset Processor");
- icon = IDI_BALL_PENDING;
- break;
- case EConnectionState::Listening:
- if (m_connectedToAssetProcessor)
- {
- m_connectedToAssetProcessor = false;
- m_showAPDisconnectDialog = true;
- }
- tooltip = tr("Listening for incoming connections");
- icon = IDI_BALL_PENDING;
- break;
- case EConnectionState::Connected:
- m_connectedToAssetProcessor = true;
- tooltip = tr("Connected to Asset Processor");
- icon = IDI_BALL_ONLINE;
- break;
- case EConnectionState::Disconnected:
- icon = IDI_BALL_OFFLINE;
- tooltip = tr("Disconnected from Asset Processor");
- break;
- }
- if (m_connectedToAssetProcessor)
- {
- m_connectionLostTimer->stop();
- }
- tooltip += "\n Last Asset Processor Task: ";
- tooltip += m_connectionListener->LastAssetProcessorTask().c_str();
- tooltip += "\n";
- AZStd::set<AZStd::string> failedJobs = m_connectionListener->FailedJobsList();
- int failureCount = static_cast<int>(failedJobs.size());
- if (failureCount)
- {
- tooltip += "\n Failed Jobs\n";
- for (auto failedJob : failedJobs)
- {
- tooltip += failedJob.c_str();
- tooltip += "\n";
- }
- }
- status = tr("Pending Jobs : %1 Failed Jobs : %2").arg(m_connectionListener->GetJobsCount()).arg(failureCount);
- statusBar->SetItem("connection", status, tooltip, icon);
- if (m_showAPDisconnectDialog && m_connectionListener->GetState() != EConnectionState::Connected)
- {
- m_showAPDisconnectDialog = false;// Just show the dialog only once if connection is lost
- m_connectionLostTimer->setSingleShot(true);
- m_connectionLostTimer->start(15000);
- }
- }
- }
- void MainWindow::ShowConnectionDisconnectedDialog()
- {
- // when REMOTE_ASSET_PROCESSOR is undef'd it means behave as if there is no such thing as the remote asset processor.
- #ifdef REMOTE_ASSET_PROCESSOR
- if (gEnv && gEnv->pSystem)
- {
- QMessageBox messageBox(this);
- messageBox.setWindowTitle(tr("Asset Processor has disconnected."));
- messageBox.setText(
- tr("Asset Processor is not connected. Please try (re)starting the Asset Processor or restarting the Editor.<br><br>"
- "Data may be lost while the Asset Processor is not running!<br>"
- "The status of the Asset Processor can be monitored from the editor in the bottom-right corner of the status bar.<br><br>"
- "Would you like to start the asset processor?<br>"));
- messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Ignore);
- messageBox.setDefaultButton(QMessageBox::Yes);
- messageBox.setIcon(QMessageBox::Critical);
- if (messageBox.exec() == QMessageBox::Yes)
- {
- AzFramework::AssetSystem::LaunchAssetProcessor();
- }
- }
- else
- {
- QMessageBox::critical(this, tr("Asset Processor has disconnected."),
- tr("Asset Processor is not connected. Please try (re)starting the asset processor or restarting the Editor.<br><br>"
- "Data may be lost while the asset processor is not running!<br>"
- "The status of the asset processor can be monitored from the editor in the bottom-right corner of the status bar."));
- }
- #endif
- }
- void MainWindow::OnConnectionStatusClicked()
- {
- AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystemRequestBus::Events::ShowAssetProcessor);
- }
- static bool paneLessThan(const QtViewPane& v1, const QtViewPane& v2)
- {
- return v1.m_name.compare(v2.m_name, Qt::CaseInsensitive) < 0;
- }
- void MainWindow::RegisterOpenWndCommands()
- {
- s_openViewCmds.clear();
- auto panes = m_viewPaneManager->GetRegisteredPanes(/* viewPaneMenuOnly=*/ false);
- std::sort(panes.begin(), panes.end(), paneLessThan);
- for (auto viewPane : panes)
- {
- if (viewPane.m_category.isEmpty())
- {
- continue;
- }
- const QString className = viewPane.m_name;
- // Make a open-view command for the class.
- QString classNameLowered = viewPane.m_name.toLower();
- classNameLowered.replace(' ', '_');
- QString openCommandName = "open_";
- openCommandName += classNameLowered;
- CEditorOpenViewCommand* pCmd = new CEditorOpenViewCommand(GetIEditor(), viewPane.m_name);
- s_openViewCmds.push_back(pCmd);
- CCommand0::SUIInfo cmdUI;
- cmdUI.caption = className.toUtf8().data();
- cmdUI.tooltip = (QString("Open ") + className).toUtf8().data();
- cmdUI.iconFilename = className.toUtf8().data();
- GetIEditor()->GetCommandManager()->RegisterUICommand("editor", openCommandName.toUtf8().data(),
- "", "", [pCmd] { pCmd->Execute(); }, cmdUI);
- GetIEditor()->GetCommandManager()->GetUIInfo("editor", openCommandName.toUtf8().data(), cmdUI);
- }
- }
- bool MainWindow::event(QEvent* event)
- {
- #ifdef Q_OS_MAC
- if (event->type() == QEvent::HoverMove)
- {
- // this fixes a problem on macOS where the mouse cursor was not
- // set when hovering over the splitter handles between dock widgets
- // it might be fixed in future Qt versions
- auto mouse = static_cast<QHoverEvent*>(event);
- bool result = QMainWindow::event(event);
- void setCocoaMouseCursor(QWidget*);
- setCocoaMouseCursor(childAt(mouse->pos()));
- return result;
- }
- #endif
- return QMainWindow::event(event);
- }
- void MainWindow::ToggleConsole()
- {
- m_viewPaneManager->TogglePane(LyViewPane::Console);
- QtViewPane* pane = m_viewPaneManager->GetPane(LyViewPane::Console);
- if (!pane)
- {
- return;
- }
- // If we toggled the console on, we want to focus its input text field
- if (pane->IsVisible())
- {
- CConsoleSCB* console = qobject_cast<CConsoleSCB*>(pane->Widget());
- if (!console)
- {
- return;
- }
- console->SetInputFocus();
- }
- }
- void MainWindow::ConnectivityStateChanged(const AzToolsFramework::SourceControlState state)
- {
- bool connected = false;
- ISourceControl* pSourceControl = GetIEditor() ? GetIEditor()->GetSourceControl() : nullptr;
- if (pSourceControl)
- {
- pSourceControl->SetSourceControlState(state);
- if (state == AzToolsFramework::SourceControlState::Active || state == AzToolsFramework::SourceControlState::ConfigurationInvalid)
- {
- connected = true;
- }
- }
- gSettings.enableSourceControl = connected;
- gSettings.SaveEnableSourceControlFlag(false);
- gSettings.SaveSettingsRegistryFile();
- }
- void MainWindow::OnGotoSelected()
- {
- AzToolsFramework::EditorRequestBus::Broadcast(&AzToolsFramework::EditorRequestBus::Events::GoToSelectedEntitiesInViewports);
- }
- void MainWindow::OnGotoSliceRoot()
- {
- int numViews = GetIEditor()->GetViewManager()->GetViewCount();
- for (int i = 0; i < numViews; ++i)
- {
- CViewport* viewport = GetIEditor()->GetViewManager()->GetView(i);
- if (viewport)
- {
- viewport->CenterOnSliceInstance();
- }
- }
- }
- // 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
- // and are reading it as an event rather.
- void MainWindow::keyPressEvent(QKeyEvent* e)
- {
- // We shouldn't need to do this, as there's already an escape key shortcut set on an action
- // attached to the MainWindow. We need to explicitly trap the escape key here because when in
- // Game Mode, all of the actions attached to the MainWindow are disabled.
- if (e->key() == Qt::Key_Escape)
- {
- MainWindow::OnEscapeAction();
- return;
- }
- return QMainWindow::keyPressEvent(e);
- }
- void MainWindow::dragEnterEvent(QDragEnterEvent *event)
- {
- using namespace AzQtComponents;
- DragAndDropContextBase context;
- DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::DragEnter, event, context);
- }
- void MainWindow::dragMoveEvent(QDragMoveEvent* event)
- {
- using namespace AzQtComponents;
- DragAndDropContextBase context;
- DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::DragMove, event, context);
- }
- void MainWindow::dragLeaveEvent(QDragLeaveEvent* event)
- {
- using namespace AzQtComponents;
- DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::DragLeave, event);
- }
- void MainWindow::dropEvent(QDropEvent *event)
- {
- using namespace AzQtComponents;
- DragAndDropContextBase context;
- DragAndDropEventsBus::Event(DragAndDropContexts::EditorMainWindow, &DragAndDropEvents::Drop, event, context);
- }
- bool MainWindow::focusNextPrevChild(bool next)
- {
- // Don't change the focus when we're in game mode or else the viewport could
- // stop receiving input events
- if (GetIEditor()->IsInGameMode())
- {
- return false;
- }
- return QMainWindow::focusNextPrevChild(next);
- }
- namespace AzToolsFramework
- {
- void MainWindowEditorFuncsHandler::Reflect(AZ::ReflectContext* context)
- {
- if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
- {
- // this will put these methods into the 'azlmbr.legacy.general' module
- auto addLegacyGeneral = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
- {
- methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
- ->Attribute(AZ::Script::Attributes::Category, "Legacy/Editor")
- ->Attribute(AZ::Script::Attributes::Module, "legacy.general");
- };
- addLegacyGeneral(behaviorContext->Method("open_pane", PyOpenViewPane, nullptr, "Opens a view pane specified by the pane class name."));
- addLegacyGeneral(behaviorContext->Method("close_pane", PyCloseViewPane, nullptr, "Closes a view pane specified by the pane class name."));
- addLegacyGeneral(behaviorContext->Method("is_pane_visible", PyIsViewPaneVisible, nullptr, "Returns true if pane specified by the pane class name is visible."));
- addLegacyGeneral(behaviorContext->Method("get_pane_class_names", PyGetViewPaneNames, nullptr, "Get all available class names for use with open_pane & close_pane."));
- addLegacyGeneral(behaviorContext->Method("exit", PyExit, nullptr, "Exits the editor."));
- addLegacyGeneral(behaviorContext->Method("exit_no_prompt", PyExitNoPrompt, nullptr, "Exits the editor without prompting to save first."));
- addLegacyGeneral(behaviorContext->Method("test_output", PyTestOutput, nullptr, "Report test information."));
- }
- }
- }
- #include <moc_MainWindow.cpp>
|