EditorToolsApplication.cpp 13 KB

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