UiEditorEntityContext.cpp 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128
  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 "EditorCommon.h"
  9. #include <AzCore/Component/Entity.h>
  10. #include <AzCore/Component/EntityUtils.h>
  11. #include <AzCore/Component/ComponentApplicationBus.h>
  12. #include <AzCore/Component/TransformBus.h>
  13. #include <AzCore/Component/ComponentExport.h>
  14. #include <AzCore/Serialization/SerializeContext.h>
  15. #include <AzCore/IO/ByteContainerStream.h>
  16. #include <AzCore/IO/FileIO.h>
  17. #include <AzCore/Serialization/Utils.h>
  18. #include <AzCore/Math/Transform.h>
  19. #include <AzCore/Asset/AssetManager.h>
  20. #include <AzFramework/API/ApplicationAPI.h>
  21. #include <AzFramework/Entity/EntityContext.h>
  22. #include <AzFramework/Entity/GameEntityContextBus.h>
  23. #include <AzFramework/Asset/AssetCatalogBus.h>
  24. #include <AzFramework/StringFunc/StringFunc.h>
  25. #include <AzToolsFramework/Slice/SliceCompilation.h>
  26. #include <AzToolsFramework/ToolsComponents/EditorOnlyEntityComponent.h>
  27. #include <AzToolsFramework/API/EntityCompositionRequestBus.h>
  28. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  29. #include <LyShine/Bus/UiElementBus.h>
  30. #include <LyShine/Bus/UiTransformBus.h>
  31. #include <LyShine/Bus/Tools/UiSystemToolsBus.h>
  32. #include <LyShine/UiComponentTypes.h>
  33. #include "UiEditorEntityContext.h"
  34. namespace Internal
  35. {
  36. void RemoveIncompatibleComponents(AZ::Entity* entity)
  37. {
  38. const AZ::Entity::ComponentArrayType components = entity->GetComponents();
  39. AZ::Entity::ComponentArrayType validComponents;
  40. AZ::Entity::ComponentArrayType incompatibleComponents;
  41. AZ::ComponentDescriptor::DependencyArrayType incompatibleServices;
  42. AZ::ComponentDescriptor::DependencyArrayType providedServices;
  43. AZStd::string incompatibleNames;
  44. for (auto component : components)
  45. {
  46. AZ::ComponentDescriptor* testComponentDesc = nullptr;
  47. AZ::ComponentDescriptorBus::EventResult(testComponentDesc, azrtti_typeid(component), &AZ::ComponentDescriptorBus::Events::GetDescriptor);
  48. providedServices.clear();
  49. testComponentDesc->GetProvidedServices(providedServices, component);
  50. bool isIncompatible = false;
  51. for (auto validComponent : validComponents)
  52. {
  53. AZ::ComponentDescriptor* validComponentDesc = nullptr;
  54. AZ::ComponentDescriptorBus::EventResult(validComponentDesc, azrtti_typeid(validComponent), &AZ::ComponentDescriptorBus::Events::GetDescriptor);
  55. incompatibleServices.clear();
  56. validComponentDesc->GetIncompatibleServices(incompatibleServices, validComponent);
  57. auto foundItr = AZStd::find_first_of(incompatibleServices.begin(), incompatibleServices.end(), providedServices.begin(), providedServices.end());
  58. if (foundItr != incompatibleServices.end())
  59. {
  60. isIncompatible = true;
  61. break;
  62. }
  63. }
  64. if (isIncompatible)
  65. {
  66. incompatibleComponents.push_back(component);
  67. incompatibleNames.append(testComponentDesc->GetName());
  68. incompatibleNames += '\n';
  69. }
  70. else
  71. {
  72. validComponents.push_back(component);
  73. }
  74. }
  75. // Should be safe to remove components, because the entity hasn't been activated.
  76. for (auto componentToRemove : incompatibleComponents)
  77. {
  78. entity->RemoveComponent(componentToRemove);
  79. }
  80. AZ_Error("UiCanvas", incompatibleComponents.empty(), "The following incompatible component(s) are removed from the entity %s:\n%s", entity->GetName().c_str(), incompatibleNames.c_str());
  81. }
  82. }
  83. ////////////////////////////////////////////////////////////////////////////////////////////////////
  84. UiEditorEntityContext::UiEditorEntityContext(EditorWindow* editorWindow)
  85. : m_editorWindow(editorWindow)
  86. , m_requiredEditorComponentTypes
  87. ({
  88. azrtti_typeid<AzToolsFramework::Components::EditorOnlyEntityComponent>()
  89. })
  90. {
  91. }
  92. ////////////////////////////////////////////////////////////////////////////////////////////////////
  93. UiEditorEntityContext::~UiEditorEntityContext()
  94. {
  95. }
  96. ////////////////////////////////////////////////////////////////////////////////////////////////////
  97. bool UiEditorEntityContext::HandleLoadedRootSliceEntity(AZ::Entity* rootEntity, bool remapIds, AZ::SliceComponent::EntityIdToEntityIdMap* idRemapTable)
  98. {
  99. AZ_Assert(m_entityOwnershipService->IsInitialized(), "The context has not been initialized.");
  100. bool rootEntityReloadSuccessful = false;
  101. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootEntityReloadSuccessful, GetContextId(),
  102. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::HandleRootEntityReloadedFromStream, rootEntity, remapIds, idRemapTable);
  103. if (!rootEntityReloadSuccessful)
  104. {
  105. return false;
  106. }
  107. AZ::SliceComponent::EntityList entities;
  108. m_entityOwnershipService->GetAllEntities(entities);
  109. AzFramework::SliceEntityOwnershipServiceRequestBus::Event(GetContextId(),
  110. &AzFramework::SliceEntityOwnershipServiceRequests::SetIsDynamic, true);
  111. InitializeEntities(entities);
  112. return true;
  113. }
  114. ////////////////////////////////////////////////////////////////////////////////////////////////////
  115. void UiEditorEntityContext::InitUiContext()
  116. {
  117. m_entityOwnershipService = AZStd::make_unique<AzFramework::SliceEntityOwnershipService>(GetContextId(), GetSerializeContext());
  118. InitContext();
  119. // Since root asset initialization happens in EntityOwnershipService and since this class is not inheriting from it,
  120. // we need to now connect to the asset bus using the root asset id here.
  121. AzFramework::RootSliceAsset rootSliceAsset;
  122. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSliceAsset, GetContextId(),
  123. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootAsset);
  124. m_rootAssetId = rootSliceAsset->GetId();
  125. AZ::Data::AssetBus::MultiHandler::BusConnect(m_rootAssetId);
  126. m_entityOwnershipService->InstantiateAllPrefabs();
  127. UiEntityContextRequestBus::Handler::BusConnect(GetContextId());
  128. UiEditorEntityContextRequestBus::Handler::BusConnect(GetContextId());
  129. AzToolsFramework::EditorEntityContextPickingRequestBus::Handler::BusConnect(GetContextId());
  130. }
  131. ////////////////////////////////////////////////////////////////////////////////////////////////////
  132. void UiEditorEntityContext::DestroyUiContext()
  133. {
  134. UiEditorEntityContextRequestBus::Handler::BusDisconnect();
  135. UiEntityContextRequestBus::Handler::BusDisconnect();
  136. AzToolsFramework::EditorEntityContextPickingRequestBus::Handler::BusDisconnect();
  137. AZ::Data::AssetBus::MultiHandler::BusDisconnect(m_rootAssetId);
  138. DestroyContext();
  139. }
  140. ////////////////////////////////////////////////////////////////////////////////////////////////////
  141. bool UiEditorEntityContext::SaveToStreamForGame(AZ::IO::GenericStream& stream, AZ::DataStream::StreamType streamType)
  142. {
  143. AZ::SliceComponent::EntityList sourceEntities;
  144. m_entityOwnershipService->GetAllEntities(sourceEntities);
  145. // Create a source slice from our editor components.
  146. AZ::Entity* sourceSliceEntity = aznew AZ::Entity();
  147. AZ::SliceComponent* sourceSliceData = sourceSliceEntity->CreateComponent<AZ::SliceComponent>();
  148. AZ::Data::Asset<AZ::SliceAsset> sourceSliceAsset(aznew AZ::SliceAsset(), AZ::Data::AssetLoadBehavior::Default);
  149. sourceSliceAsset.Get()->SetData(sourceSliceEntity, sourceSliceData);
  150. for (AZ::Entity* sourceEntity : sourceEntities)
  151. {
  152. sourceSliceData->AddEntity(sourceEntity);
  153. }
  154. // Emulate client flags.
  155. AZ::PlatformTagSet platformTags = { AZ_CRC_CE("renderer") };
  156. // Compile the source slice into the runtime slice (with runtime components).
  157. AzToolsFramework::UiEditorOnlyEntityHandler uiEditorOnlyEntityHandler;
  158. AzToolsFramework::EditorOnlyEntityHandlers handlers =
  159. {
  160. &uiEditorOnlyEntityHandler,
  161. };
  162. AzToolsFramework::SliceCompilationResult sliceCompilationResult = CompileEditorSlice(sourceSliceAsset, platformTags, *m_serializeContext, handlers);
  163. // Reclaim entities from the temporary source asset.
  164. for (AZ::Entity* sourceEntity : sourceEntities)
  165. {
  166. sourceSliceData->RemoveEntity(sourceEntity, false);
  167. }
  168. if (!sliceCompilationResult)
  169. {
  170. m_errorMessage = sliceCompilationResult.GetError();
  171. return false;
  172. }
  173. // Export runtime slice representing the level, which is a completely flat list of entities.
  174. AZ::Data::Asset<AZ::SliceAsset> exportSliceAsset = sliceCompilationResult.GetValue();
  175. AZ::Entity* exportSliceAssetEntity = exportSliceAsset.Get()->GetEntity();
  176. const bool saveObjectSuccess = AZ::Utils::SaveObjectToStream<AZ::Entity>(stream, streamType, exportSliceAssetEntity);
  177. AZ::SliceComponent* sliceComponent = exportSliceAssetEntity->FindComponent<AZ::SliceComponent>();
  178. AZ::SliceComponent::EntityList sliceEntities;
  179. const bool getEntitiesSuccess = sliceComponent->GetEntities(sliceEntities);
  180. const bool sliceEntitiesValid = getEntitiesSuccess && sliceEntities.size() > 0;
  181. if (!sliceEntitiesValid)
  182. {
  183. AZ_Error("Save Runtime Stream", false, "Failed to export entities for runtime:\n%s", sliceCompilationResult.GetError().c_str());
  184. return false;
  185. }
  186. return saveObjectSuccess;
  187. }
  188. ////////////////////////////////////////////////////////////////////////////////////////////////////
  189. bool UiEditorEntityContext::SaveCanvasEntityToStreamForGame(AZ::Entity* canvasEntity, AZ::IO::GenericStream& stream, AZ::DataStream::StreamType streamType)
  190. {
  191. AZ::Entity* sourceCanvasEntity = canvasEntity;
  192. AZ::Entity* exportCanvasEntity = aznew AZ::Entity(sourceCanvasEntity->GetName().c_str());
  193. exportCanvasEntity->SetId(sourceCanvasEntity->GetId());
  194. AZ_Assert(exportCanvasEntity, "Failed to create target entity \"%s\" for export.",
  195. sourceCanvasEntity->GetName().c_str());
  196. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
  197. &AzToolsFramework::ToolsApplicationRequests::Bus::Events::PreExportEntity, *sourceCanvasEntity, *exportCanvasEntity);
  198. // Export entity representing the canvas, which has only runtime components.
  199. AZ::Utils::SaveObjectToStream<AZ::Entity>(stream, streamType, exportCanvasEntity);
  200. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
  201. &AzToolsFramework::ToolsApplicationRequests::Bus::Events::PostExportEntity, *sourceCanvasEntity, *exportCanvasEntity);
  202. return true;
  203. }
  204. ////////////////////////////////////////////////////////////////////////////////////////////////////
  205. AZ::Entity* UiEditorEntityContext::CreateUiEntity(const char* name)
  206. {
  207. AZ::Entity* entity = CreateEntity(name);
  208. if (entity)
  209. {
  210. // we don't currently do anything extra here, UI entities are not automatically
  211. // Init'ed and Activate'd when they are created. We wait until the required components
  212. // are added before Init and Activate
  213. }
  214. return entity;
  215. }
  216. ////////////////////////////////////////////////////////////////////////////////////////////////////
  217. AZ::SliceComponent* UiEditorEntityContext::GetUiRootSlice()
  218. {
  219. AZ::SliceComponent* rootSlice = nullptr;
  220. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSlice, GetContextId(),
  221. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootSlice);
  222. return rootSlice;
  223. }
  224. ////////////////////////////////////////////////////////////////////////////////////////////////////
  225. void UiEditorEntityContext::AddUiEntity(AZ::Entity* entity)
  226. {
  227. AZ_Assert(entity, "Supplied entity is invalid.");
  228. AddEntity(entity);
  229. }
  230. ////////////////////////////////////////////////////////////////////////////////////////////////////
  231. void UiEditorEntityContext::AddUiEntities(const AzFramework::EntityList& entities)
  232. {
  233. for (AZ::Entity* entity : entities)
  234. {
  235. AZ_Assert(!AzFramework::EntityIdContextQueryBus::MultiHandler::BusIsConnectedId(entity->GetId()), "Entity already in context.");
  236. AzFramework::RootSliceAsset rootSliceAsset;
  237. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSliceAsset, GetContextId(),
  238. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootAsset);
  239. rootSliceAsset->GetComponent()->AddEntity(entity);
  240. }
  241. m_entityOwnershipService->HandleEntitiesAdded(entities);
  242. }
  243. ////////////////////////////////////////////////////////////////////////////////////////////////////
  244. bool UiEditorEntityContext::CloneUiEntities(const AZStd::vector<AZ::EntityId>& sourceEntities, AzFramework::EntityList& resultEntities)
  245. {
  246. resultEntities.clear();
  247. AZ::SliceComponent::InstantiatedContainer sourceObjects(false);
  248. for (const AZ::EntityId& id : sourceEntities)
  249. {
  250. AZ::Entity* entity = nullptr;
  251. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, id);
  252. if (entity)
  253. {
  254. sourceObjects.m_entities.push_back(entity);
  255. }
  256. }
  257. AZ::SliceComponent::EntityIdToEntityIdMap idMap;
  258. AZ::SliceComponent::InstantiatedContainer* clonedObjects =
  259. AZ::EntityUtils::CloneObjectAndFixEntities(&sourceObjects, idMap);
  260. if (!clonedObjects)
  261. {
  262. AZ_Error("UiEntityContext", false, "Failed to clone source entities.");
  263. return false;
  264. }
  265. resultEntities = clonedObjects->m_entities;
  266. AddUiEntities(resultEntities);
  267. clonedObjects->m_deleteEntitiesOnDestruction = false;
  268. delete clonedObjects;
  269. return true;
  270. }
  271. ////////////////////////////////////////////////////////////////////////////////////////////////////
  272. bool UiEditorEntityContext::DestroyUiEntity(AZ::EntityId entityId)
  273. {
  274. return DestroyEntityById(entityId);
  275. }
  276. ////////////////////////////////////////////////////////////////////////////////////////////////////
  277. bool UiEditorEntityContext::SupportsViewportEntityIdPicking()
  278. {
  279. return true;
  280. }
  281. ////////////////////////////////////////////////////////////////////////////////////////////////////
  282. AZ::SliceComponent::SliceInstanceAddress UiEditorEntityContext::CloneEditorSliceInstance(
  283. [[maybe_unused]] AZ::SliceComponent::SliceInstanceAddress sourceInstance)
  284. {
  285. return AZ::SliceComponent::SliceInstanceAddress();
  286. }
  287. ////////////////////////////////////////////////////////////////////////////////////////////////////
  288. AzFramework::SliceInstantiationTicket UiEditorEntityContext::InstantiateEditorSlice(const AZ::Data::Asset<AZ::Data::AssetData>& sliceAsset, AZ::Vector2 viewportPosition)
  289. {
  290. return InstantiateEditorSliceAtChildIndex(sliceAsset, viewportPosition, -1);
  291. }
  292. ////////////////////////////////////////////////////////////////////////////////////////////////////
  293. AzFramework::SliceInstantiationTicket UiEditorEntityContext::InstantiateEditorSliceAtChildIndex(const AZ::Data::Asset<AZ::Data::AssetData>& sliceAsset,
  294. AZ::Vector2 viewportPosition,
  295. int childIndex)
  296. {
  297. if (sliceAsset.GetId().IsValid())
  298. {
  299. InstantiatingEditorSliceParams instantiatingSliceParams(viewportPosition, childIndex);
  300. m_instantiatingSlices.push_back(AZStd::make_pair(sliceAsset, instantiatingSliceParams));
  301. AzFramework::SliceInstantiationTicket ticket;
  302. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(ticket, GetContextId(),
  303. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::InstantiateSlice, sliceAsset, nullptr, nullptr);
  304. if (ticket.IsValid())
  305. {
  306. AzFramework::SliceInstantiationResultBus::MultiHandler::BusConnect(ticket);
  307. }
  308. return ticket;
  309. }
  310. return AzFramework::SliceInstantiationTicket();
  311. }
  312. ////////////////////////////////////////////////////////////////////////////////////////////////////
  313. void UiEditorEntityContext::RestoreSliceEntity(AZ::Entity* entity, const AZ::SliceComponent::EntityRestoreInfo& info)
  314. {
  315. AZ_Error("EditorEntityContext", info.m_assetId.IsValid(), "Invalid asset Id for entity restore.");
  316. // If asset isn't loaded when this request is made, we need to queue the load and process the request
  317. // when the asset is ready. Otherwise we'll immediately process the request when OnAssetReady is invoked
  318. // by the AssetBus connection policy.
  319. AZ::Data::Asset<AZ::Data::AssetData> asset =
  320. AZ::Data::AssetManager::Instance().GetAsset<AZ::SliceAsset>(info.m_assetId, AZ::Data::AssetLoadBehavior::Default);
  321. SliceEntityRestoreRequest request = {entity, info, asset};
  322. m_queuedSliceEntityRestores.emplace_back(request);
  323. AZ::Data::AssetBus::MultiHandler::BusConnect(asset.GetId());
  324. }
  325. ////////////////////////////////////////////////////////////////////////////////////////////////////
  326. void UiEditorEntityContext::QueueSliceReplacement(const char* targetPath,
  327. const AZStd::unordered_map<AZ::EntityId, AZ::EntityId>& selectedToAssetMap,
  328. const AZStd::unordered_set<AZ::EntityId>& entitiesInSelection,
  329. AZ::Entity* commonParent, AZ::Entity* insertBefore)
  330. {
  331. AZ_Error("EditorEntityContext", m_queuedSliceReplacement.m_path.empty(), "A slice replacement is already on the queue.");
  332. m_queuedSliceReplacement.Setup(targetPath, selectedToAssetMap, entitiesInSelection, commonParent, insertBefore);
  333. AzFramework::AssetCatalogEventBus::Handler::BusConnect();
  334. }
  335. ////////////////////////////////////////////////////////////////////////////////////////////////////
  336. void UiEditorEntityContext::DeleteElements(AzToolsFramework::EntityIdList elements)
  337. {
  338. // Deletes the specified elements using an undoable command
  339. if (elements.size() > 0)
  340. {
  341. HierarchyWidget* hierarchy = m_editorWindow->GetHierarchy();
  342. // Get the list of currently selected entities so that we can attempt to restore that
  343. // after the delete (the undoable command currently only works on selected entities)
  344. QTreeWidgetItemRawPtrQList selection = hierarchy->selectedItems();
  345. EntityHelpers::EntityIdList selectedEntities = SelectionHelpers::GetSelectedElementIds(hierarchy, selection, false);
  346. // Make sure elements still exist. There is a situation related to "Push to Slice" where an
  347. // element to be deleted may no longer exist. This occurs if a new child slice instance is
  348. // pushed to its parent slice, then "undo" is performed which brings back the child instance
  349. // that was deleted during the "Push to Slice" process, and then the recovered child instance
  350. // is pushed to its parent slice again
  351. elements.erase(
  352. AZStd::remove_if(
  353. elements.begin(), elements.end(),
  354. [](AZ::EntityId entityId)
  355. {
  356. AZ::Entity* entity = nullptr;
  357. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
  358. return !entity;
  359. }),
  360. elements.end());
  361. if (elements.empty())
  362. {
  363. return;
  364. }
  365. // Use an undoable command to delete the entities
  366. // The way the command is implemented depends upon selecting the items first
  367. HierarchyHelpers::SetSelectedItems(hierarchy, &elements);
  368. CommandHierarchyItemDelete::Push(m_editorWindow->GetActiveStack(),
  369. hierarchy,
  370. hierarchy->selectedItems());
  371. // Attempt to set the selection back to what it was but first remove any items from the selected
  372. // list that no longer exist
  373. selectedEntities.erase(
  374. std::remove_if(
  375. selectedEntities.begin(), selectedEntities.end(),
  376. [](AZ::EntityId entityId)
  377. {
  378. AZ::Entity* entity = nullptr;
  379. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
  380. return !entity;
  381. }),
  382. selectedEntities.end());
  383. HierarchyHelpers::SetSelectedItems(hierarchy, &selectedEntities);
  384. }
  385. }
  386. ////////////////////////////////////////////////////////////////////////////////////////////////////
  387. bool UiEditorEntityContext::HasPendingRequests()
  388. {
  389. if (!m_queuedSliceEntityRestores.empty())
  390. {
  391. return true;
  392. }
  393. return false;
  394. }
  395. ////////////////////////////////////////////////////////////////////////////////////////////////////
  396. bool UiEditorEntityContext::IsInstantiatingSlices()
  397. {
  398. if (!m_instantiatingSlices.empty())
  399. {
  400. return true;
  401. }
  402. return false;
  403. }
  404. ////////////////////////////////////////////////////////////////////////////////////////////////////
  405. void UiEditorEntityContext::DetachSliceEntities(const AzToolsFramework::EntityIdList& entities)
  406. {
  407. if (entities.empty())
  408. {
  409. return;
  410. }
  411. for (const AZ::EntityId& entityId : entities)
  412. {
  413. AZ::SliceComponent::SliceInstanceAddress sliceAddress;
  414. AzFramework::SliceEntityRequestBus::EventResult(sliceAddress, entityId,
  415. &AzFramework::SliceEntityRequestBus::Events::GetOwningSlice);
  416. if (sliceAddress.IsValid())
  417. {
  418. AZ::Entity* entity = nullptr;
  419. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId);
  420. AZ_Error("EditorEntityContext", entity, "Unable to find entity for EntityID %llu", entityId);
  421. if (entity)
  422. {
  423. if (sliceAddress.GetReference()->GetSliceComponent()->RemoveEntity(entityId, false)) // Remove from current slice instance without deleting
  424. {
  425. AZ::SliceComponent* rootSlice = nullptr;
  426. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSlice, GetContextId(),
  427. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootSlice);
  428. rootSlice->AddEntity(entity); // Add back as loose entity
  429. }
  430. }
  431. }
  432. }
  433. }
  434. ////////////////////////////////////////////////////////////////////////////////////////////////////
  435. void UiEditorEntityContext::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
  436. {
  437. if (m_queuedSliceReplacement.IsValid())
  438. {
  439. AZStd::string relativePath;
  440. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  441. relativePath, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetPathById, assetId);
  442. if (AZStd::string::npos != AzFramework::StringFunc::Find(m_queuedSliceReplacement.m_path.c_str(), relativePath.c_str()))
  443. {
  444. AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
  445. AZStd::unordered_set<AZ::EntityId> topLevelEntities;
  446. GetTopLevelEntities(m_queuedSliceReplacement.m_entitiesInSelection, topLevelEntities);
  447. // Request the slice instantiation.
  448. AZ::Data::Asset<AZ::Data::AssetData> asset =
  449. AZ::Data::AssetManager::Instance().FindOrCreateAsset<AZ::SliceAsset>(assetId, AZ::Data::AssetLoadBehavior::Default);
  450. AZ::Vector2 viewportPosition(-1.0f, -1.0f);
  451. m_queuedSliceReplacement.m_ticket = InstantiateEditorSlice(asset, viewportPosition);
  452. }
  453. }
  454. }
  455. ////////////////////////////////////////////////////////////////////////////////////////////////////
  456. void UiEditorEntityContext::ResetContext()
  457. {
  458. // First deactivate all the entities, before calling the base class ResetContext which will
  459. // delete them all.
  460. // This helps us know that we do not need to maintain the cached pointers between the UiElementComponents
  461. // as individual elements are destroyed.
  462. AZ::SliceComponent::EntityList entities;
  463. bool result = m_entityOwnershipService->GetAllEntities(entities);
  464. if (result)
  465. {
  466. for (AZ::Entity* entity : entities)
  467. {
  468. if (entity->GetState() == AZ::Entity::State::Active)
  469. {
  470. entity->Deactivate();
  471. }
  472. }
  473. }
  474. // Now reset the context which will destroy all the entities
  475. EntityContext::ResetContext();
  476. }
  477. ////////////////////////////////////////////////////////////////////////////////////////////////////
  478. void UiEditorEntityContext::OnSlicePreInstantiate([[maybe_unused]] const AZ::Data::AssetId& sliceAssetId, [[maybe_unused]] const AZ::SliceComponent::SliceInstanceAddress& sliceAddress)
  479. {
  480. // For UI slices we don't need to do anything here. The main EditorEntityContextComponent
  481. // changes the transforms here. But we need the entities to be initialized and activated
  482. // before recalculating offsets so we do it in OnSliceInstantiated.
  483. }
  484. ////////////////////////////////////////////////////////////////////////////////////////////////////
  485. void UiEditorEntityContext::OnSliceInstantiated(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress)
  486. {
  487. AZ::Data::AssetBus::MultiHandler::BusConnect(sliceAssetId);
  488. const AzFramework::SliceInstantiationTicket ticket = *AzFramework::SliceInstantiationResultBus::GetCurrentBusId();
  489. // If we got here by creating a new slice then we have extra work to do (deleting the old entities etc)
  490. AZ::Entity* insertBefore = nullptr;
  491. if (ticket == m_queuedSliceReplacement.m_ticket)
  492. {
  493. m_queuedSliceReplacement.Finalize(sliceAddress, m_editorWindow);
  494. // Select the common parent (the call to Finalize will have deleted the elements that were selected)
  495. m_editorWindow->GetHierarchy()->SetUniqueSelectionHighlight(m_queuedSliceReplacement.m_commonParent);
  496. insertBefore = m_queuedSliceReplacement.m_insertBefore;
  497. }
  498. AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect(ticket);
  499. // Close out the next ticket corresponding to this asset.
  500. for (auto instantiatingIter = m_instantiatingSlices.begin(); instantiatingIter != m_instantiatingSlices.end(); ++instantiatingIter)
  501. {
  502. if (instantiatingIter->first.GetId() == sliceAssetId)
  503. {
  504. const AZ::SliceComponent::EntityList& entities = sliceAddress.GetInstance()->GetInstantiated()->m_entities;
  505. if (entities.size() == 0)
  506. {
  507. // if there are no entities there was an error with the instantiation
  508. AZ::Data::AssetBus::MultiHandler::BusDisconnect(sliceAssetId);
  509. UiEditorEntityContextNotificationBus::Broadcast(&UiEditorEntityContextNotificationBus::Events::OnSliceInstantiationFailed, sliceAssetId, ticket);
  510. m_instantiatingSlices.erase(instantiatingIter);
  511. break;
  512. }
  513. // Initialize the new entities and create a set of all the top-level entities.
  514. AZStd::unordered_set<AZ::Entity*> topLevelEntities;
  515. for (AZ::Entity* entity : entities)
  516. {
  517. if (entity->GetState() == AZ::Entity::State::Constructed)
  518. {
  519. entity->Init();
  520. }
  521. if (entity->GetState() == AZ::Entity::State::Init)
  522. {
  523. entity->Activate();
  524. }
  525. topLevelEntities.insert(entity);
  526. }
  527. // remove anything from the topLevelEntities set that is referenced as the child of another element in the list
  528. for (AZ::Entity* entity : entities)
  529. {
  530. LyShine::EntityArray children;
  531. UiElementBus::EventResult(children, entity->GetId(), &UiElementBus::Events::GetChildElements);
  532. for (auto child : children)
  533. {
  534. topLevelEntities.erase(child);
  535. }
  536. }
  537. // This can be null if nothing is selected. That is OK, the usage of it below treats that as meaning
  538. // add as a child of the root element.
  539. AZ::Entity* parent = m_editorWindow->GetHierarchy()->CurrentSelectedElement();
  540. int childIndex = instantiatingIter->second.m_childIndex;
  541. if (!insertBefore && childIndex >= 0)
  542. {
  543. if (parent)
  544. {
  545. UiElementBus::EventResult(insertBefore, parent->GetId(), &UiElementBus::Events::GetChildElement, childIndex);
  546. }
  547. else
  548. {
  549. UiCanvasBus::EventResult(insertBefore, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::GetChildElement, childIndex);
  550. }
  551. }
  552. // Now topLevelElements contains all of the top-level elements in the set of newly instantiated entities
  553. // Copy the topLevelEntities set into a list
  554. LyShine::EntityArray entitiesToInit;
  555. for (auto entity : topLevelEntities)
  556. {
  557. entitiesToInit.push_back(entity);
  558. }
  559. // There must be at least one element
  560. AZ_Assert(entitiesToInit.size() >= 1, "There must be at least one top-level entity in a UI slice.");
  561. // Initialize the internal parent pointers and the canvas pointer in the elements
  562. // We do this before adding the elements, otherwise the GetUniqueChildName code in FixupCreatedEntities will
  563. // already see the new elements and think the names are not unique
  564. UiCanvasBus::Event(m_editorWindow->GetCanvas(), &UiCanvasBus::Events::FixupCreatedEntities, entitiesToInit, true, parent);
  565. // Add all of the top-level entities as children of the parent
  566. for (auto entity : topLevelEntities)
  567. {
  568. UiCanvasBus::Event(m_editorWindow->GetCanvas(), &UiCanvasBus::Events::AddElement, entity, parent, insertBefore);
  569. }
  570. // Here we adjust the position of the instantiated entities so that if the slice was instantiated from the
  571. // viewport menu we instantiate it at the mouse position
  572. AZ::Vector2 desiredViewportPosition = instantiatingIter->second.m_viewportPosition;
  573. if (desiredViewportPosition != AZ::Vector2(-1.0f, -1.0f))
  574. {
  575. // This is the same behavior as the old "Add elements from prefab" had.
  576. AZ::Entity* rootElement = entitiesToInit[0];
  577. // Transform pivot position to canvas space
  578. AZ::Vector2 pivotPos;
  579. UiTransformBus::EventResult(pivotPos, rootElement->GetId(), &UiTransformBus::Events::GetCanvasSpacePivotNoScaleRotate);
  580. // Transform destination position to canvas space
  581. AZ::Matrix4x4 transformFromViewport;
  582. UiTransformBus::Event(rootElement->GetId(), &UiTransformBus::Events::GetTransformFromViewport, transformFromViewport);
  583. AZ::Vector3 destPos3 = transformFromViewport * AZ::Vector3(desiredViewportPosition.GetX(), desiredViewportPosition.GetY(), 0.0f);
  584. AZ::Vector2 destPos(destPos3.GetX(), destPos3.GetY());
  585. AZ::Vector2 offsetDelta = destPos - pivotPos;
  586. // Adjust offsets on all top level elements
  587. for (auto entity : entitiesToInit)
  588. {
  589. UiTransform2dInterface::Offsets offsets;
  590. UiTransform2dBus::EventResult(offsets, entity->GetId(), &UiTransform2dBus::Events::GetOffsets);
  591. UiTransform2dBus::Event(entity->GetId(), &UiTransform2dBus::Events::SetOffsets, offsets + offsetDelta);
  592. }
  593. }
  594. // the entities have already been created but we need to make an undo command that can undo/redo that action
  595. HierarchyWidget* hierarchyWidget = m_editorWindow->GetHierarchy();
  596. QTreeWidgetItemRawPtrQList selectedItems = hierarchyWidget->selectedItems();
  597. // use an undoable command to create the elements from the slice
  598. CommandHierarchyItemCreateFromData::Push(m_editorWindow->GetActiveStack(),
  599. hierarchyWidget,
  600. selectedItems,
  601. true,
  602. [ topLevelEntities ]([[maybe_unused]] HierarchyItem* parent, LyShine::EntityArray& listOfNewlyCreatedTopLevelElements)
  603. {
  604. for (AZ::Entity* entity : topLevelEntities)
  605. {
  606. listOfNewlyCreatedTopLevelElements.push_back(entity);
  607. }
  608. },
  609. "Instantiate Slice");
  610. m_instantiatingSlices.erase(instantiatingIter);
  611. UiEditorEntityContextNotificationBus::Broadcast(
  612. &UiEditorEntityContextNotificationBus::Events::OnSliceInstantiated, sliceAssetId, sliceAddress, ticket);
  613. break;
  614. }
  615. }
  616. }
  617. ////////////////////////////////////////////////////////////////////////////////////////////////////
  618. void UiEditorEntityContext::OnSliceInstantiationFailed(const AZ::Data::AssetId& sliceAssetId)
  619. {
  620. const AzFramework::SliceInstantiationTicket ticket = *AzFramework::SliceInstantiationResultBus::GetCurrentBusId();
  621. AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect(ticket);
  622. for (auto instantiatingIter = m_instantiatingSlices.begin(); instantiatingIter != m_instantiatingSlices.end(); ++instantiatingIter)
  623. {
  624. if (instantiatingIter->first.GetId() == sliceAssetId)
  625. {
  626. AZ::Data::AssetBus::MultiHandler::BusDisconnect(sliceAssetId);
  627. UiEditorEntityContextNotificationBus::Broadcast(
  628. &UiEditorEntityContextNotificationBus::Events::OnSliceInstantiationFailed, sliceAssetId, ticket);
  629. m_instantiatingSlices.erase(instantiatingIter);
  630. break;
  631. }
  632. }
  633. }
  634. ////////////////////////////////////////////////////////////////////////////////////////////////////
  635. void UiEditorEntityContext::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  636. {
  637. // We want to stay connected to the asset bus for root uicontext asset to listen to any changes to prefab assets in the ui canvas.
  638. if (m_rootAssetId.IsValid() && asset.GetId() == m_rootAssetId)
  639. {
  640. return;
  641. }
  642. AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
  643. for (auto iter = m_queuedSliceEntityRestores.begin(); iter != m_queuedSliceEntityRestores.end(); )
  644. {
  645. SliceEntityRestoreRequest& request = *iter;
  646. if (asset.GetId() == request.m_asset.GetId())
  647. {
  648. AZ::SliceComponent* rootSlice = nullptr;
  649. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSlice, GetContextId(),
  650. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootSlice);
  651. AZ::SliceComponent::SliceInstanceAddress address = rootSlice->RestoreEntity(request.m_entity, request.m_restoreInfo);
  652. // Note that we do not add the entity to the context/rootSlice using AddEntity here.
  653. // This is because it has already been added to the root slice as a prefab instance.
  654. // Instead we call HandleEntitiesAdded which just adds it to the context
  655. if (address.IsValid())
  656. {
  657. m_entityOwnershipService->HandleEntitiesAdded({request.m_entity});
  658. }
  659. else
  660. {
  661. AZ_Error("EditorEntityContext", false, "Failed to restore entity \"%s\" [%llu]",
  662. request.m_entity->GetName().c_str(), request.m_entity->GetId());
  663. delete request.m_entity;
  664. }
  665. iter = m_queuedSliceEntityRestores.erase(iter);
  666. }
  667. else
  668. {
  669. ++iter;
  670. }
  671. }
  672. // Pass on to base Entity Ownership Service.
  673. m_entityOwnershipService->OnAssetReady(asset);
  674. }
  675. ////////////////////////////////////////////////////////////////////////////////////////////////////
  676. // Root slice (or its dependents) has been reloaded.
  677. void UiEditorEntityContext::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
  678. {
  679. bool isActive = false;
  680. if (m_editorWindow->GetEntityContext() && m_editorWindow->GetEntityContext()->GetContextId() == GetContextId())
  681. {
  682. isActive = true;
  683. }
  684. HierarchyWidget* hierarchy = nullptr;
  685. EntityHelpers::EntityIdList selectedEntities;
  686. if (isActive)
  687. {
  688. hierarchy = m_editorWindow->GetHierarchy();
  689. const QTreeWidgetItemRawPtrQList& selection = hierarchy->selectedItems();
  690. selectedEntities = SelectionHelpers::GetSelectedElementIds(hierarchy, selection, false);
  691. // This ensures there's no "current item".
  692. hierarchy->SetUniqueSelectionHighlight((QTreeWidgetItem*)nullptr);
  693. // IMPORTANT: This is necessary to indirectly trigger detach()
  694. // in the PropertiesWidget.
  695. hierarchy->SetUserSelection(nullptr);
  696. }
  697. m_entityOwnershipService->OnAssetReloaded(asset);
  698. UiCanvasBus::Event(m_editorWindow->GetCanvasForEntityContext(GetContextId()), &UiCanvasBus::Events::ReinitializeElements);
  699. if (isActive)
  700. {
  701. // Ensure selection set is preserved after applying the new level slice.
  702. // But make sure we don't add any EntityId to selection that no longer exists as that cause a crash later
  703. selectedEntities.erase(
  704. std::remove_if(
  705. selectedEntities.begin(), selectedEntities.end(),
  706. [](AZ::EntityId entityId)
  707. {
  708. AZ::Entity* entity = nullptr;
  709. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
  710. return !entity;
  711. }),
  712. selectedEntities.end());
  713. // Refresh the Hierarchy pane
  714. LyShine::EntityArray childElements;
  715. UiCanvasBus::EventResult(childElements, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::GetChildElements);
  716. hierarchy->RecreateItems(childElements);
  717. HierarchyHelpers::SetSelectedItems(hierarchy, &selectedEntities);
  718. }
  719. // We want to update the status for any tabs being used to edit slices.
  720. // If that tab has just done a push, we want to check at this point whether there are any differences between the
  721. // reloaded asset and the instance.
  722. m_editorWindow->UpdateChangedStatusOnAssetChange(GetContextId(), asset);
  723. }
  724. //////////////////////////////////////////////////////////////////////////
  725. void UiEditorEntityContext::OnContextEntitiesAdded(const AzFramework::EntityList& entities)
  726. {
  727. EntityContext::OnContextEntitiesAdded(entities);
  728. InitializeEntities(entities);
  729. }
  730. //////////////////////////////////////////////////////////////////////////
  731. bool UiEditorEntityContext::ValidateEntitiesAreValidForContext(const AzFramework::EntityList& entities)
  732. {
  733. // All entities in a slice being instantiated in the UI editor should
  734. // have the UiElementComponent on them.
  735. for (AZ::Entity* entity : entities)
  736. {
  737. if (!entity->FindComponent(LyShine::UiElementComponentUuid))
  738. {
  739. return false;
  740. }
  741. }
  742. return true;
  743. }
  744. ////////////////////////////////////////////////////////////////////////////////////////////////////
  745. void UiEditorEntityContext::SetupUiEntity(AZ::Entity* entity)
  746. {
  747. InitializeEntities({ entity });
  748. }
  749. ////////////////////////////////////////////////////////////////////////////////////////////////////
  750. void UiEditorEntityContext::InitializeEntities(const AzFramework::EntityList& entities)
  751. {
  752. // UI entities are now automatically activated on creation
  753. for (AZ::Entity* entity : entities)
  754. {
  755. if (entity->GetState() == AZ::Entity::State::Constructed)
  756. {
  757. entity->Init();
  758. }
  759. }
  760. // Add required editor components to entities
  761. for (AZ::Entity* entity : entities)
  762. {
  763. for (const auto& componentType : m_requiredEditorComponentTypes)
  764. {
  765. if (!entity->FindComponent(componentType))
  766. {
  767. entity->CreateComponent(componentType);
  768. }
  769. }
  770. }
  771. for (AZ::Entity* entity : entities)
  772. {
  773. if (entity->GetState() == AZ::Entity::State::Init)
  774. {
  775. // Always invalidate the entity dependencies when loading in the editor
  776. // (we don't know what code has changed since the last time the editor was run and the services provided/required
  777. // by entities might have changed)
  778. entity->InvalidateDependencies();
  779. // Because we automatically add the EditorOnlyEntityComponent if it doesn't exist, we can encounter a situation
  780. // where an entity has duplicate EditorOnlyEntityComponents if an old canvas is resaved and an old slice it uses
  781. // is also resaved. See LY-90580
  782. // In the main editor this is handled by disabling the duplicate components, but the UI Editor doesn't use that
  783. // method (the world editor allows the user to manually add incompatible components and then disable and enable
  784. // them in the entity, the UI Editor still works how the world editor used to - it doesn't allow users to add
  785. // incompatible components and has no way to disable/enable components in the property pane).
  786. // So we do automatic recovery in the case where there are duplicate EditorOnlyEntityComponents. We have to do this
  787. // before activating in order to avoid errors being reported.
  788. AZ::Entity::ComponentArrayType editorOnlyEntityComponents =
  789. entity->FindComponents(AzToolsFramework::Components::EditorOnlyEntityComponent::TYPEINFO_Uuid());
  790. if (editorOnlyEntityComponents.size() > 1)
  791. {
  792. // There are duplicate EditorOnlyEntityComponents. If any of them have m_isEditorOnly set to true we will
  793. // set the one we keep to true. The reasoning is that these duplicates only happen when canvases and slices
  794. // are being gradually resaved to the new version with EditorOnlyEntityComponents. Since the default is false,
  795. // if we find one set to true this is more likely to be one that the user specifically set that way.
  796. bool isEditorOnly = false;
  797. for (int i = 0; i < editorOnlyEntityComponents.size(); ++i)
  798. {
  799. AzToolsFramework::Components::EditorOnlyEntityComponent* thisComponent =
  800. static_cast<AzToolsFramework::Components::EditorOnlyEntityComponent*>(editorOnlyEntityComponents[i]);
  801. if (thisComponent->IsEditorOnlyEntity())
  802. {
  803. isEditorOnly = true;
  804. break;
  805. }
  806. }
  807. // We are going to keep the first one, ensure that its value of m_isEditorOnly is set the right way
  808. if (isEditorOnly)
  809. {
  810. AzToolsFramework::Components::EditorOnlyEntityComponent* firstComponent =
  811. static_cast<AzToolsFramework::Components::EditorOnlyEntityComponent*>(editorOnlyEntityComponents[0]);
  812. if (!firstComponent->IsEditorOnlyEntity())
  813. {
  814. firstComponent->SetIsEditorOnlyEntity(true);
  815. }
  816. }
  817. // Now remove all the components except the first one. The first one will be the one from the most deeply nested
  818. // slice. It is best to keep that one, otherwise we end up with local slice overrides deleting the components from
  819. // the instanced slices which means we could ignore changes from the slice when we should not.
  820. for (int i = 1; i < editorOnlyEntityComponents.size(); ++i)
  821. {
  822. AZ::Component* duplicateComponent = editorOnlyEntityComponents[i];
  823. entity->RemoveComponent(duplicateComponent);
  824. delete duplicateComponent;
  825. }
  826. }
  827. // This is a temporary solution to remove incompatible components so that the entity can
  828. // activate properly, otherwise all sorts of bad things will happen.
  829. //
  830. // We do have formal way to handle invalid components for Editor entities (see EditorEntityActionComponent::ScrubEntities()).
  831. // But it requires components being derived from EditorComponentBase. UiCanvas doesn't seem to distinguish between game-time
  832. // and editor-time components, so we can't use the existing scrubbing method.
  833. Internal::RemoveIncompatibleComponents(entity);
  834. entity->Activate();
  835. }
  836. }
  837. }
  838. //////////////////////////////////////////////////////////////////////////
  839. void UiEditorEntityContext::GetTopLevelEntities(const AZStd::unordered_set<AZ::EntityId>& entities, AZStd::unordered_set<AZ::EntityId>& topLevelEntities)
  840. {
  841. for (auto entityId : entities)
  842. {
  843. // if this entities parent is not in the set then it is a top-level
  844. AZ::Entity* parentElement = nullptr;
  845. UiElementBus::EventResult(parentElement, entityId, &UiElementBus::Events::GetParent);
  846. if (!parentElement || entities.count(parentElement->GetId()) == 0)
  847. {
  848. topLevelEntities.insert(entityId);
  849. }
  850. }
  851. }
  852. ////////////////////////////////////////////////////////////////////////////////////////////////////
  853. bool UiEditorEntityContext::QueuedSliceReplacement::IsValid() const
  854. {
  855. return !m_path.empty();
  856. }
  857. ////////////////////////////////////////////////////////////////////////////////////////////////////
  858. void UiEditorEntityContext::QueuedSliceReplacement::Reset()
  859. {
  860. m_path.clear();
  861. }
  862. ////////////////////////////////////////////////////////////////////////////////////////////////////
  863. void UiEditorEntityContext::QueuedSliceReplacement::Finalize(
  864. const AZ::SliceComponent::SliceInstanceAddress& instanceAddress,
  865. EditorWindow* editorWindow)
  866. {
  867. AZ::SliceComponent::EntityAncestorList ancestors;
  868. AZStd::unordered_map<AZ::EntityId, AZ::EntityId> remapIds;
  869. const auto& newEntities = instanceAddress.GetInstance()->GetInstantiated()->m_entities;
  870. // Store mapping between live Ids we're out to remove, and the ones now provided by
  871. // the slice instance, so we can fix up references on any still-external entities.
  872. for (const AZ::Entity* newEntity : newEntities)
  873. {
  874. ancestors.clear();
  875. instanceAddress.GetReference()->GetInstanceEntityAncestry(newEntity->GetId(), ancestors, 1);
  876. AZ_Error("EditorEntityContext", !ancestors.empty(), "Failed to locate ancestor for newly created slice entity.");
  877. if (!ancestors.empty())
  878. {
  879. for (const auto& pair : m_selectedToAssetMap)
  880. {
  881. const AZ::EntityId& ancestorId = ancestors.front().m_entity->GetId();
  882. if (pair.second == ancestorId)
  883. {
  884. remapIds[pair.first] = newEntity->GetId();
  885. break;
  886. }
  887. }
  888. }
  889. }
  890. AZ::SerializeContext* serializeContext = nullptr;
  891. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  892. // Remap references on any entities left out of the slice, to any entities in the slice instance.
  893. for (const AZ::EntityId& selectedId : m_entitiesInSelection)
  894. {
  895. if (m_selectedToAssetMap.find(selectedId) != m_selectedToAssetMap.end())
  896. {
  897. // Entity is included in the slice; no need to patch.
  898. continue;
  899. }
  900. AZ::Entity* entity = nullptr;
  901. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, selectedId);
  902. AZ_Error("EditorEntityContext", entity, "Failed to locate live entity during slice replacement.");
  903. if (entity)
  904. {
  905. entity->Deactivate();
  906. AZ::EntityUtils::ReplaceEntityRefs(entity,
  907. [&remapIds](const AZ::EntityId& originalId, bool /*isEntityId*/) -> AZ::EntityId
  908. {
  909. auto iter = remapIds.find(originalId);
  910. if (iter == remapIds.end())
  911. {
  912. return originalId;
  913. }
  914. else
  915. {
  916. return iter->second;
  917. }
  918. }, serializeContext);
  919. entity->Activate();
  920. }
  921. }
  922. // Delete the entities from the world that were used to create the slice, since the slice
  923. // will be instantiated to replace them.
  924. AZStd::vector<AZ::EntityId> deleteEntityIds;
  925. deleteEntityIds.reserve(m_selectedToAssetMap.size());
  926. for (const auto& pair : m_selectedToAssetMap)
  927. {
  928. deleteEntityIds.push_back(pair.first);
  929. }
  930. // Use an undoable command to delete the entities
  931. HierarchyWidget* hierarchy = editorWindow->GetHierarchy();
  932. CommandHierarchyItemDelete::Push(editorWindow->GetActiveStack(),
  933. hierarchy,
  934. hierarchy->selectedItems());
  935. // This ensures there's no "current item".
  936. hierarchy->SetUniqueSelectionHighlight((QTreeWidgetItem*)nullptr);
  937. // IMPORTANT: This is necessary to indirectly trigger detach()
  938. // in the PropertiesWidget.
  939. hierarchy->SetUserSelection(nullptr);
  940. Reset();
  941. }