EditorToolsApplication.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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 "EditorToolsApplication.h"
  10. // Qt
  11. #include <QMessageBox>
  12. // AzToolsFramework
  13. #include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
  14. #include <AzToolsFramework/Thumbnails/ThumbnailerComponent.h>
  15. #include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
  16. // Editor
  17. #include "MainWindow.h"
  18. #include "Controls/ReflectedPropertyControl/ReflectedVar.h"
  19. #include "CryEdit.h"
  20. #include "DisplaySettingsPythonFuncs.h"
  21. #include "GameEngine.h"
  22. #include "PythonEditorFuncs.h"
  23. #include "TrackView/TrackViewPythonFuncs.h"
  24. #include "Include/IObjectManager.h"
  25. #include "Objects/ObjectManager.h"
  26. namespace EditorInternal
  27. {
  28. EditorToolsApplication::EditorToolsApplication(int* argc, char*** argv)
  29. : ToolsApplication(argc, argv)
  30. {
  31. EditorToolsApplicationRequests::Bus::Handler::BusConnect();
  32. AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusConnect();
  33. AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusConnect();
  34. }
  35. EditorToolsApplication::~EditorToolsApplication()
  36. {
  37. AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusDisconnect();
  38. AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusDisconnect();
  39. EditorToolsApplicationRequests::Bus::Handler::BusDisconnect();
  40. Stop();
  41. }
  42. bool EditorToolsApplication::IsStartupAborted() const
  43. {
  44. return m_StartupAborted;
  45. }
  46. void EditorToolsApplication::RegisterCoreComponents()
  47. {
  48. AzToolsFramework::ToolsApplication::RegisterCoreComponents();
  49. // Expose Legacy Python Bindings to Behavior Context for EditorPythonBindings Gem
  50. RegisterComponentDescriptor(AzToolsFramework::CryEditPythonHandler::CreateDescriptor());
  51. RegisterComponentDescriptor(AzToolsFramework::CryEditDocFuncsHandler::CreateDescriptor());
  52. RegisterComponentDescriptor(AzToolsFramework::DisplaySettingsPythonFuncsHandler::CreateDescriptor());
  53. RegisterComponentDescriptor(AzToolsFramework::MainWindowEditorFuncsHandler::CreateDescriptor());
  54. RegisterComponentDescriptor(AzToolsFramework::ObjectManagerFuncsHandler::CreateDescriptor());
  55. RegisterComponentDescriptor(AzToolsFramework::PythonEditorComponent::CreateDescriptor());
  56. RegisterComponentDescriptor(AzToolsFramework::PythonEditorFuncsHandler::CreateDescriptor());
  57. RegisterComponentDescriptor(AzToolsFramework::DisplaySettingsComponent::CreateDescriptor());
  58. RegisterComponentDescriptor(AzToolsFramework::TrackViewComponent::CreateDescriptor());
  59. RegisterComponentDescriptor(AzToolsFramework::TrackViewFuncsHandler::CreateDescriptor());
  60. RegisterComponentDescriptor(AzToolsFramework::ViewPanePythonFuncsHandler::CreateDescriptor());
  61. RegisterComponentDescriptor(AzToolsFramework::ViewportTitleDlgPythonFuncsHandler::CreateDescriptor());
  62. }
  63. AZ::ComponentTypeList EditorToolsApplication::GetRequiredSystemComponents() const
  64. {
  65. AZ::ComponentTypeList components = AzToolsFramework::ToolsApplication::GetRequiredSystemComponents();
  66. components.emplace_back(azrtti_typeid<AzToolsFramework::Thumbnailer::ThumbnailerComponent>());
  67. components.emplace_back(azrtti_typeid<AzToolsFramework::AssetBrowser::AssetBrowserComponent>());
  68. // Add new Bus-based Python Bindings
  69. components.emplace_back(azrtti_typeid<AzToolsFramework::DisplaySettingsComponent>());
  70. components.emplace_back(azrtti_typeid<AzToolsFramework::PythonEditorComponent>());
  71. components.emplace_back(azrtti_typeid<AzToolsFramework::TrackViewComponent>());
  72. return components;
  73. }
  74. void EditorToolsApplication::StartCommon(AZ::Entity* systemEntity)
  75. {
  76. AzToolsFramework::ToolsApplication::StartCommon(systemEntity);
  77. m_StartupAborted = m_moduleManager->m_quitRequested;
  78. if (systemEntity->GetState() != AZ::Entity::State::Active)
  79. {
  80. m_StartupAborted = true;
  81. }
  82. }
  83. bool EditorToolsApplication::Start()
  84. {
  85. AzFramework::Application::StartupParameters params;
  86. // Must be done before creating QApplication, otherwise asserts when we alloc
  87. AzToolsFramework::ToolsApplication::Start({}, params);
  88. if (IsStartupAborted() || !m_systemEntity)
  89. {
  90. AzToolsFramework::ToolsApplication::Stop();
  91. return false;
  92. }
  93. return true;
  94. }
  95. void EditorToolsApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const
  96. {
  97. appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Editor | AZ::ApplicationTypeQuery::Masks::Tool;
  98. };
  99. void EditorToolsApplication::CreateReflectionManager()
  100. {
  101. ToolsApplication::CreateReflectionManager();
  102. GetSerializeContext()->CreateEditContext();
  103. }
  104. void EditorToolsApplication::Reflect(AZ::ReflectContext* context)
  105. {
  106. ToolsApplication::Reflect(context);
  107. // Reflect property control classes to the serialize context...
  108. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  109. {
  110. ReflectedVarInit::setupReflection(serializeContext);
  111. }
  112. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  113. {
  114. behaviorContext->EBus<EditorToolsApplicationRequestBus>("EditorToolsApplicationRequestBus")
  115. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  116. ->Attribute(AZ::Script::Attributes::Category, "Editor")
  117. ->Attribute(AZ::Script::Attributes::Module, "editor")
  118. ->Event("OpenLevel", &EditorToolsApplicationRequests::OpenLevel)
  119. ->Event("OpenLevelNoPrompt", &EditorToolsApplicationRequests::OpenLevelNoPrompt)
  120. ->Event("CreateLevel", &EditorToolsApplicationRequests::CreateLevel)
  121. ->Event("CreateLevelNoPrompt", &EditorToolsApplicationRequests::CreateLevelNoPrompt)
  122. ->Event("GetGameFolder", &EditorToolsApplicationRequests::GetGameFolder)
  123. ->Event("GetCurrentLevelName", &EditorToolsApplicationRequests::GetCurrentLevelName)
  124. ->Event("GetCurrentLevelPath", &EditorToolsApplicationRequests::GetCurrentLevelPath)
  125. ->Event("Exit", &EditorToolsApplicationRequests::Exit)
  126. ->Event("ExitNoPrompt", &EditorToolsApplicationRequests::ExitNoPrompt)
  127. ;
  128. }
  129. }
  130. AZStd::string EditorToolsApplication::GetGameFolder() const
  131. {
  132. return Path::GetEditingGameDataFolder();
  133. }
  134. bool EditorToolsApplication::OpenLevel(AZStd::string_view levelName)
  135. {
  136. AZStd::string levelPath = levelName;
  137. if (!AZ::IO::SystemFile::Exists(levelPath.c_str()))
  138. {
  139. // now let's check if they pre-pended directories (Samples/SomeLevelName)
  140. AZStd::string levelFileName;
  141. {
  142. AZStd::size_t found = levelPath.find_last_of("/\\");
  143. if (found == AZStd::string::npos)
  144. {
  145. levelFileName = levelPath.substr(found + 1);
  146. }
  147. else
  148. {
  149. levelFileName = levelPath;
  150. }
  151. }
  152. // if the input path can't be found, let's automatically add on the game folder and the levels
  153. AzFramework::StringFunc::Path::Join(levelPath.c_str(), levelFileName.c_str(), levelPath);
  154. AzFramework::StringFunc::Path::Join(DefaultLevelFolder, levelPath.c_str(), levelPath);
  155. AzFramework::StringFunc::Path::Join(GetGameFolder().c_str(), levelPath.c_str(), levelPath);
  156. // make sure the level path includes the cry extension, if needed
  157. if (!levelFileName.ends_with(GetOldCryLevelExtension()) && !levelFileName.ends_with(GetLevelExtension()))
  158. {
  159. AZStd::size_t levelPathLength = levelPath.length();
  160. levelPath += GetOldCryLevelExtension();
  161. // Check if there is a .cry file, otherwise assume it is a new .ly file
  162. if (!AZ::IO::SystemFile::Exists(levelPath.c_str()))
  163. {
  164. levelPath.replace(levelPathLength, sizeof(GetOldCryLevelExtension()) - 1, GetLevelExtension());
  165. }
  166. }
  167. if (!AZ::IO::SystemFile::Exists(levelPath.c_str()))
  168. {
  169. return false;
  170. }
  171. }
  172. auto previousDocument = GetIEditor()->GetDocument();
  173. QString previousPathName = (previousDocument != nullptr) ? previousDocument->GetLevelPathName() : "";
  174. auto newDocument = CCryEditApp::instance()->OpenDocumentFile(levelPath.c_str(), true, COpenSameLevelOptions::ReopenLevelIfSame);
  175. // the underlying document pointer doesn't change, so we can't check that; use the path name's instead
  176. return newDocument && !newDocument->IsLevelLoadFailed();
  177. }
  178. bool EditorToolsApplication::OpenLevelNoPrompt(AZStd::string_view levelName)
  179. {
  180. GetIEditor()->GetDocument()->SetModifiedFlag(false);
  181. return OpenLevel(levelName);
  182. }
  183. int EditorToolsApplication::CreateLevel(AZStd::string_view levelName, bool /*bUseTerrain*/)
  184. {
  185. // Clang warns about a temporary being created in a function's argument list, so fullyQualifiedLevelName before the call
  186. QString fullyQualifiedLevelName;
  187. return CCryEditApp::instance()->CreateLevel(QString::fromUtf8(levelName.data(), static_cast<int>(levelName.size())), fullyQualifiedLevelName);
  188. }
  189. int EditorToolsApplication::CreateLevelNoPrompt(AZStd::string_view levelName, int /*terrainExportTextureSize*/, bool /*useTerrain*/)
  190. {
  191. // If a level was open, ignore any unsaved changes if it had been modified
  192. if (GetIEditor()->IsLevelLoaded())
  193. {
  194. GetIEditor()->GetDocument()->SetModifiedFlag(false);
  195. }
  196. // Clang warns about a temporary being created in a function's argument list, so fullyQualifiedLevelName before the call
  197. QString fullyQualifiedLevelName;
  198. return CCryEditApp::instance()->CreateLevel(QString::fromUtf8(levelName.data(), static_cast<int>(levelName.size())), fullyQualifiedLevelName);
  199. }
  200. AZStd::string EditorToolsApplication::GetCurrentLevelName() const
  201. {
  202. return AZStd::string(GetIEditor()->GetGameEngine()->GetLevelName().toUtf8().data());
  203. }
  204. AZStd::string EditorToolsApplication::GetCurrentLevelPath() const
  205. {
  206. return AZStd::string(GetIEditor()->GetGameEngine()->GetLevelPath().toUtf8().data());
  207. }
  208. const char* EditorToolsApplication::GetLevelExtension() const
  209. {
  210. bool prefabSystemEnabled = false;
  211. AzFramework::ApplicationRequests::Bus::BroadcastResult(
  212. prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
  213. if (!prefabSystemEnabled)
  214. {
  215. return ".ly";
  216. }
  217. else
  218. {
  219. return ".prefab";
  220. }
  221. }
  222. const char* EditorToolsApplication::GetOldCryLevelExtension() const
  223. {
  224. return ".cry";
  225. }
  226. void EditorToolsApplication::Exit()
  227. {
  228. // Adding a single-shot QTimer to PyExit delays the QApplication::closeAllWindows call until
  229. // all the events in the event queue have been processed. Calling QApplication::closeAllWindows instead
  230. // of MainWindow::close ensures the Metal render window is cleaned up on macOS.
  231. QTimer::singleShot(0, qApp, &QApplication::closeAllWindows);
  232. }
  233. void EditorToolsApplication::ExitNoPrompt()
  234. {
  235. // Set the level to "unmodified" so that it doesn't prompt to save on exit.
  236. GetIEditor()->GetDocument()->SetModifiedFlag(false);
  237. Exit();
  238. }
  239. AzToolsFramework::ViewportInteraction::KeyboardModifiers EditorToolsApplication::QueryKeyboardModifiers()
  240. {
  241. return AzToolsFramework::ViewportInteraction::BuildKeyboardModifiers(QGuiApplication::queryKeyboardModifiers());
  242. }
  243. AZStd::chrono::milliseconds EditorToolsApplication::EditorViewportInputTimeNow()
  244. {
  245. const auto now = AZStd::chrono::steady_clock::now();
  246. return AZStd::chrono::time_point_cast<AZStd::chrono::milliseconds>(now).time_since_epoch();
  247. }
  248. } // namespace EditorInternal