HierarchyWidget.cpp 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273
  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 "AssetDropHelpers.h"
  10. #include "QtHelpers.h"
  11. #include <AzCore/Asset/AssetManager.h>
  12. #include <AzToolsFramework/ToolsComponents/EditorEntityIdContainer.h>
  13. #include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
  14. #include <AzToolsFramework/ToolsComponents/EditorOnlyEntityComponentBus.h>
  15. #include <QApplication>
  16. #include <QMessageBox>
  17. #include <QMouseEvent>
  18. #include <QMimeData>
  19. #include <QDropEvent>
  20. #include <QDragEnterEvent>
  21. HierarchyWidget::HierarchyWidget(EditorWindow* editorWindow)
  22. : AzQtComponents::StyledTreeWidget()
  23. , m_isDeleting(false)
  24. , m_editorWindow(editorWindow)
  25. , m_entityItemMap()
  26. , m_itemBeingHovered(nullptr)
  27. , m_inDragStartState(false)
  28. , m_selectionChangedBeforeDrag(false)
  29. , m_signalSelectionChange(true)
  30. , m_inObjectPickMode(false)
  31. , m_isInited(false)
  32. {
  33. setMouseTracking(true);
  34. // Style.
  35. {
  36. setAcceptDrops(true);
  37. setDropIndicatorShown(true);
  38. setDragEnabled(true);
  39. setDragDropMode(QAbstractItemView::DragDrop);
  40. setSelectionMode(QAbstractItemView::ExtendedSelection);
  41. setColumnCount(kHierarchyColumnCount);
  42. setHeader(new HierarchyHeader(this));
  43. // IMPORTANT: This MUST be done here.
  44. // This CAN'T be done inside HierarchyHeader.
  45. header()->setSectionsClickable(true);
  46. header()->setSectionResizeMode(kHierarchyColumnName, QHeaderView::Stretch);
  47. header()->setSectionResizeMode(kHierarchyColumnIsVisible, QHeaderView::Fixed);
  48. header()->setSectionResizeMode(kHierarchyColumnIsSelectable, QHeaderView::Fixed);
  49. // This controls the width of the last 2 columns; both in the header and in the body of the HierarchyWidget.
  50. header()->resizeSection(kHierarchyColumnIsVisible, UICANVASEDITOR_HIERARCHY_HEADER_ICON_SIZE);
  51. header()->resizeSection(kHierarchyColumnIsSelectable, UICANVASEDITOR_HIERARCHY_HEADER_ICON_SIZE);
  52. }
  53. // Connect signals.
  54. {
  55. // Selection change notification.
  56. QObject::connect(selectionModel(),
  57. SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
  58. SLOT(CurrentSelectionHasChanged(const QItemSelection &, const QItemSelection &)));
  59. QObject::connect(model(),
  60. SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QVector<int> &)),
  61. SLOT(DataHasChanged(const QModelIndex &, const QModelIndex &, const QVector<int> &)));
  62. }
  63. QObject::connect(this,
  64. &QTreeWidget::itemClicked,
  65. this,
  66. [this](QTreeWidgetItem* item, int column)
  67. {
  68. HierarchyItem* i = HierarchyItem::RttiCast(item);
  69. if (column == kHierarchyColumnIsVisible)
  70. {
  71. ToggleVisibility(i);
  72. }
  73. else if (column == kHierarchyColumnIsSelectable)
  74. {
  75. CommandHierarchyItemToggleIsSelectable::Push(m_editorWindow->GetActiveStack(),
  76. this,
  77. HierarchyItemRawPtrList({i}));
  78. }
  79. else if (m_inObjectPickMode)
  80. {
  81. PickItem(i);
  82. }
  83. });
  84. QObject::connect(this,
  85. &QTreeWidget::itemExpanded,
  86. this,
  87. [this](QTreeWidgetItem* item)
  88. {
  89. CommandHierarchyItemToggleIsExpanded::Push(m_editorWindow->GetActiveStack(),
  90. this,
  91. HierarchyItem::RttiCast(item));
  92. });
  93. QObject::connect(this,
  94. &QTreeWidget::itemCollapsed,
  95. this,
  96. [this](QTreeWidgetItem* item)
  97. {
  98. CommandHierarchyItemToggleIsExpanded::Push(m_editorWindow->GetActiveStack(),
  99. this,
  100. HierarchyItem::RttiCast(item));
  101. });
  102. EntityHighlightMessages::Bus::Handler::BusConnect();
  103. }
  104. HierarchyWidget::~HierarchyWidget()
  105. {
  106. AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
  107. EntityHighlightMessages::Bus::Handler::BusDisconnect();
  108. }
  109. void HierarchyWidget::SetIsDeleting(bool b)
  110. {
  111. m_isDeleting = b;
  112. }
  113. EntityHelpers::EntityToHierarchyItemMap& HierarchyWidget::GetEntityItemMap()
  114. {
  115. return m_entityItemMap;
  116. }
  117. EditorWindow* HierarchyWidget::GetEditorWindow()
  118. {
  119. return m_editorWindow;
  120. }
  121. void HierarchyWidget::ActiveCanvasChanged()
  122. {
  123. EntityContextChanged();
  124. }
  125. void HierarchyWidget::EntityContextChanged()
  126. {
  127. if (m_inObjectPickMode)
  128. {
  129. OnEntityPickModeStopped();
  130. }
  131. // Disconnect from the PickModeRequests bus and reconnect with the new entity context
  132. AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
  133. UiEditorEntityContext* context = m_editorWindow->GetEntityContext();
  134. if (context)
  135. {
  136. AzToolsFramework::EditorPickModeNotificationBus::Handler::BusConnect(context->GetContextId());
  137. }
  138. }
  139. void HierarchyWidget::CreateItems(const LyShine::EntityArray& elements)
  140. {
  141. std::list<AZ::Entity*> elementList(elements.begin(), elements.end());
  142. // Build the rest of the list.
  143. // Note: This is a breadth-first traversal through all child elements.
  144. for (auto& e : elementList)
  145. {
  146. LyShine::EntityArray childElements;
  147. UiElementBus::EventResult(childElements, e->GetId(), &UiElementBus::Events::GetChildElements);
  148. elementList.insert(elementList.end(), childElements.begin(), childElements.end());
  149. }
  150. // Create the items.
  151. for (auto& e : elementList)
  152. {
  153. AZ::Entity* parentElement = EntityHelpers::GetParentElement(e);
  154. QTreeWidgetItem* parent = HierarchyHelpers::ElementToItem(this, parentElement, true);
  155. AZ_Assert(parent, "No parent widget item found for parent entity");
  156. int childIndex = -1;
  157. UiElementBus::EventResult(childIndex, parentElement->GetId(), &UiElementBus::Events::GetIndexOfChild, e);
  158. new HierarchyItem(m_editorWindow,
  159. *parent,
  160. childIndex,
  161. e->GetName().c_str(),
  162. e);
  163. }
  164. // restore the expanded state of all items
  165. ApplyElementIsExpanded();
  166. m_isInited = true;
  167. }
  168. void HierarchyWidget::RecreateItems(const LyShine::EntityArray& elements)
  169. {
  170. // remember the currently selected items so we can restore them
  171. EntityHelpers::EntityIdList selectedEntityIds = SelectionHelpers::GetSelectedElementIds(this,
  172. selectedItems(), false);
  173. ClearItems();
  174. CreateItems(elements);
  175. HierarchyHelpers::SetSelectedItems(this, &selectedEntityIds);
  176. }
  177. void HierarchyWidget::ClearItems()
  178. {
  179. ClearAllHierarchyItemEntityIds();
  180. // Remove all the items from the list (doesn't delete Entities since we cleared the EntityIds)
  181. clear();
  182. // The map needs to be cleared here since HandleItemRemove won't remove the map entry due to the entity Ids being cleared above
  183. m_entityItemMap.clear();
  184. m_isInited = false;
  185. }
  186. AZ::Entity* HierarchyWidget::CurrentSelectedElement() const
  187. {
  188. auto currentItem = HierarchyItem::RttiCast(QTreeWidget::currentItem());
  189. AZ::Entity* currentElement = (currentItem && currentItem->isSelected()) ? currentItem->GetElement() : nullptr;
  190. return currentElement;
  191. }
  192. void HierarchyWidget::contextMenuEvent(QContextMenuEvent* ev)
  193. {
  194. // The context menu.
  195. if (m_isInited)
  196. {
  197. HierarchyMenu contextMenu(this,
  198. (HierarchyMenu::Show::kCutCopyPaste |
  199. HierarchyMenu::Show::kNew_EmptyElement |
  200. HierarchyMenu::Show::kDeleteElement |
  201. HierarchyMenu::Show::kNewSlice |
  202. HierarchyMenu::Show::kNew_InstantiateSlice |
  203. HierarchyMenu::Show::kPushToSlice |
  204. HierarchyMenu::Show::kFindElements |
  205. HierarchyMenu::Show::kEditorOnly),
  206. true);
  207. contextMenu.exec(ev->globalPos());
  208. }
  209. QTreeWidget::contextMenuEvent(ev);
  210. }
  211. void HierarchyWidget::SignalUserSelectionHasChanged(const QTreeWidgetItemRawPtrQList& selectedItems)
  212. {
  213. HierarchyItemRawPtrList items = SelectionHelpers::GetSelectedHierarchyItems(this,
  214. selectedItems);
  215. SetUserSelection(items.empty() ? nullptr : &items);
  216. }
  217. void HierarchyWidget::CurrentSelectionHasChanged([[maybe_unused]] const QItemSelection& selected,
  218. [[maybe_unused]] const QItemSelection& deselected)
  219. {
  220. m_selectionChangedBeforeDrag = true;
  221. // IMPORTANT: This signal is triggered at the right time, but
  222. // "selected.indexes()" DOESN'T contain ALL the items currently
  223. // selected. It ONLY contains the newly selected items. To avoid
  224. // having to track what's added and removed to the selection,
  225. // we'll use selectedItems().
  226. if (m_signalSelectionChange && !m_isDeleting)
  227. {
  228. SignalUserSelectionHasChanged(selectedItems());
  229. }
  230. }
  231. void HierarchyWidget::DataHasChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, [[maybe_unused]] const QVector<int>& roles)
  232. {
  233. if (topLeft == bottomRight)
  234. {
  235. // We only care about text changes, which can ONLY be done one at a
  236. // time. This implies that topLeft must be the same as bottomRight.
  237. HierarchyItem* hierarchyItem = HierarchyItem::RttiCast(itemFromIndex(topLeft));
  238. AZ::Entity* element = hierarchyItem->GetElement();
  239. AZ_Assert(element, "No entity found for hierarchy item");
  240. AZ::EntityId entityId = element->GetId();
  241. QTreeWidgetItem* item = HierarchyHelpers::ElementToItem(this, element, false);
  242. QString toName(item ? item->text(0) : "");
  243. CommandHierarchyItemRename::Push(m_editorWindow->GetActiveStack(),
  244. this,
  245. entityId,
  246. element->GetName().c_str(),
  247. toName);
  248. }
  249. }
  250. void HierarchyWidget::HandleItemAdd(HierarchyItem* item)
  251. {
  252. m_entityItemMap[ item->GetEntityId() ] = item;
  253. }
  254. void HierarchyWidget::HandleItemRemove(HierarchyItem* item)
  255. {
  256. if (item == m_itemBeingHovered)
  257. {
  258. m_itemBeingHovered = nullptr;
  259. }
  260. m_entityItemMap.erase(item->GetEntityId());
  261. }
  262. void HierarchyWidget::ClearAllHierarchyItemEntityIds()
  263. {
  264. // as a simple way of going through all the HierarchyItem's we use the
  265. // EntityHelpers::EntityToHierarchyItemMap
  266. for (auto mapItem : m_entityItemMap)
  267. {
  268. mapItem.second->ClearEntityId();
  269. }
  270. }
  271. void HierarchyWidget::ApplyElementIsExpanded()
  272. {
  273. // Seed the list.
  274. HierarchyItemRawPtrList allItems;
  275. HierarchyHelpers::AppendAllChildrenToEndOfList(invisibleRootItem(), allItems);
  276. // Traverse the list.
  277. blockSignals(true);
  278. {
  279. HierarchyHelpers::TraverseListAndAllChildren(allItems,
  280. [](HierarchyItem* childItem)
  281. {
  282. childItem->ApplyElementIsExpanded();
  283. });
  284. }
  285. blockSignals(false);
  286. }
  287. void HierarchyWidget::mousePressEvent(QMouseEvent* ev)
  288. {
  289. m_selectionChangedBeforeDrag = false;
  290. HierarchyItem* item = HierarchyItem::RttiCast(itemAt(ev->pos()));
  291. if (!item)
  292. {
  293. // This allows the user to UNSELECT an item
  294. // by clicking in an empty area of the widget.
  295. SetUniqueSelectionHighlight((QTreeWidgetItem*)nullptr);
  296. }
  297. // Remember the selected items before the selection change in case a drag is started.
  298. // When dragging outside the hierarchy, the selection is reverted back to this selection
  299. m_beforeDragSelection = selectedItems();
  300. m_signalSelectionChange = false;
  301. QTreeWidget::mousePressEvent(ev);
  302. m_signalSelectionChange = true;
  303. }
  304. void HierarchyWidget::mouseDoubleClickEvent(QMouseEvent* ev)
  305. {
  306. HierarchyItem* item = HierarchyItem::RttiCast(itemAt(ev->pos()));
  307. if (item)
  308. {
  309. // Double-clicking to edit text is only allowed in the FIRST column.
  310. for (int col = kHierarchyColumnIsVisible; col < kHierarchyColumnCount; ++col)
  311. {
  312. QRect r = visualRect(indexFromItem(item, col));
  313. if (r.contains(ev->pos()))
  314. {
  315. // Ignore the event.
  316. return;
  317. }
  318. }
  319. }
  320. QTreeWidget::mouseDoubleClickEvent(ev);
  321. }
  322. void HierarchyWidget::startDrag(Qt::DropActions supportedActions)
  323. {
  324. // This flag is used to determine whether to perform an action on leaveEvent.
  325. // If an item is dragged really fast outside the hierarchy, this startDrag event is called,
  326. // but the dragEnterEvent and dragLeaveEvent are replaced with the leaveEvent
  327. m_inDragStartState = true;
  328. // Remember the current selection so that we can revert back to it when the items are dragged back into the hierarchy
  329. m_dragSelection = selectedItems();
  330. AzQtComponents::StyledTreeWidget::startDrag(supportedActions);
  331. }
  332. void HierarchyWidget::dragEnterEvent(QDragEnterEvent* event)
  333. {
  334. if (!AcceptsMimeData(event->mimeData()))
  335. {
  336. event->ignore();
  337. return;
  338. }
  339. if (event->mimeData()->hasFormat(AzToolsFramework::EditorEntityIdContainer::GetMimeType()))
  340. {
  341. m_inDragStartState = false;
  342. if (m_selectionChangedBeforeDrag)
  343. {
  344. m_signalSelectionChange = false;
  345. // Set the current selection to the items being dragged
  346. clearSelection();
  347. for (auto i : m_dragSelection)
  348. {
  349. i->setSelected(true);
  350. }
  351. m_signalSelectionChange = true;
  352. }
  353. }
  354. else
  355. {
  356. // Dragging an item from outside the hierarchy window
  357. m_selectionChangedBeforeDrag = false;
  358. }
  359. QTreeView::dragEnterEvent(event);
  360. }
  361. void HierarchyWidget::dragLeaveEvent(QDragLeaveEvent* event)
  362. {
  363. // This is called when dragging outside the hierarchy, or when a drag is released inside the hierarchy
  364. // but a dropEvent isn't called (ex. drop item onto itself or press Esc to cancel a drag)
  365. // Check if mouse position is inside or outside the hierarchy
  366. QRect widgetRect = geometry();
  367. QPoint mousePos = mapFromGlobal(QCursor::pos());
  368. if (widgetRect.contains(mousePos))
  369. {
  370. if (m_selectionChangedBeforeDrag)
  371. {
  372. // Treat this event as a mouse release (mouseReleaseEvent is not called in this case)
  373. SignalUserSelectionHasChanged(selectedItems());
  374. }
  375. }
  376. else
  377. {
  378. if (m_selectionChangedBeforeDrag)
  379. {
  380. m_signalSelectionChange = false;
  381. // Set the current selection to the items that were selected before the drag
  382. clearSelection();
  383. for (auto i : m_beforeDragSelection)
  384. {
  385. i->setSelected(true);
  386. }
  387. m_signalSelectionChange = true;
  388. }
  389. }
  390. QTreeView::dragLeaveEvent(event);
  391. }
  392. void HierarchyWidget::dropEvent(QDropEvent* ev)
  393. {
  394. if (!m_isInited)
  395. {
  396. return;
  397. }
  398. if (ev->mimeData()->hasFormat(AzToolsFramework::EditorEntityIdContainer::GetMimeType()))
  399. {
  400. m_inDragStartState = false;
  401. m_signalSelectionChange = false;
  402. // Get a list of selected items
  403. QTreeWidgetItemRawPtrQList selection = selectedItems();
  404. // Change current selection to only contain top level items. This avoids
  405. // the default drop behavior from changing the internal hierarchy of
  406. // the dragged elements
  407. QTreeWidgetItemRawPtrQList topLevelSelection;
  408. SelectionHelpers::GetListOfTopLevelSelectedItems(this, selection, topLevelSelection);
  409. clearSelection();
  410. for (auto i : topLevelSelection)
  411. {
  412. i->setSelected(true);
  413. }
  414. // Set current parent and child index of each selected item
  415. for (auto i : selection)
  416. {
  417. HierarchyItem* item = HierarchyItem::RttiCast(i);
  418. if (item)
  419. {
  420. QModelIndex itemIndex = indexFromItem(item);
  421. QTreeWidgetItem* baseParentItem = itemFromIndex(itemIndex.parent());
  422. if (!baseParentItem)
  423. {
  424. baseParentItem = invisibleRootItem();
  425. }
  426. HierarchyItem* parentItem = HierarchyItem::RttiCast(baseParentItem);
  427. AZ::EntityId parentId = (parentItem ? parentItem->GetEntityId() : AZ::EntityId());
  428. item->SetPreMove(parentId, itemIndex.row());
  429. }
  430. }
  431. // Do the drop event
  432. ev->setDropAction(Qt::MoveAction);
  433. QTreeWidget::dropEvent(ev);
  434. // Make a list of selected items and their parents
  435. HierarchyItemRawPtrList childItems;
  436. QTreeWidgetItemRawPtrList baseParentItems;
  437. bool itemMoved = false;
  438. for (auto i : selection)
  439. {
  440. HierarchyItem* item = HierarchyItem::RttiCast(i);
  441. if (item)
  442. {
  443. QModelIndex index = indexFromItem(item);
  444. QTreeWidgetItem* baseParentItem = itemFromIndex(index.parent());
  445. if (!baseParentItem)
  446. {
  447. baseParentItem = invisibleRootItem();
  448. }
  449. HierarchyItem* parentItem = HierarchyItem::RttiCast(baseParentItem);
  450. AZ::EntityId parentId = parentItem ? parentItem->GetEntityId() : AZ::EntityId();
  451. if ((item->GetPreMoveChildRow() != index.row()) || (item->GetPreMoveParentId() != parentId))
  452. {
  453. // Item has moved
  454. itemMoved = true;
  455. }
  456. childItems.push_back(item);
  457. baseParentItems.push_back(baseParentItem);
  458. }
  459. }
  460. if (itemMoved)
  461. {
  462. ReparentItems(baseParentItems, childItems);
  463. }
  464. else
  465. {
  466. // Items didn't move, but they became unselected so they need to be reselected
  467. for (auto i : childItems)
  468. {
  469. i->setSelected(true);
  470. }
  471. }
  472. m_signalSelectionChange = true;
  473. if (m_selectionChangedBeforeDrag)
  474. {
  475. // Signal a selection change on the mouse release
  476. SignalUserSelectionHasChanged(selectedItems());
  477. }
  478. }
  479. else if (AssetDropHelpers::DoesMimeDataContainSliceOrComponentAssets(ev->mimeData()))
  480. {
  481. DropMimeDataAssetsAtHierarchyPosition(ev->mimeData(), ev->pos());
  482. ev->setDropAction(Qt::CopyAction);
  483. ev->accept();
  484. QTreeWidget::dropEvent(ev);
  485. // Put focus on the hierarchy widget
  486. activateWindow();
  487. setFocus();
  488. }
  489. }
  490. QStringList HierarchyWidget::mimeTypes() const
  491. {
  492. QStringList list = QTreeWidget::mimeTypes();
  493. list.append(AzToolsFramework::EditorEntityIdContainer::GetMimeType());
  494. list.append(AzToolsFramework::AssetBrowser::AssetBrowserEntry::GetMimeType());
  495. return list;
  496. }
  497. QMimeData* HierarchyWidget::mimeData(const QList<QTreeWidgetItem*> items) const
  498. {
  499. AzToolsFramework::EditorEntityIdContainer entityIdList;
  500. for (auto i : items)
  501. {
  502. HierarchyItem* item = HierarchyItem::RttiCast(i);
  503. AZ::EntityId entityId = item->GetEntityId();
  504. if (entityId.IsValid())
  505. {
  506. entityIdList.m_entityIds.push_back(entityId);
  507. }
  508. }
  509. if (entityIdList.m_entityIds.empty())
  510. {
  511. return nullptr;
  512. }
  513. AZStd::vector<char> encoded;
  514. if (!entityIdList.ToBuffer(encoded))
  515. {
  516. return nullptr;
  517. }
  518. QMimeData* mimeDataPtr = new QMimeData();
  519. QByteArray encodedData;
  520. encodedData.resize((int)encoded.size());
  521. memcpy(encodedData.data(), encoded.data(), encoded.size());
  522. mimeDataPtr->setData(AzToolsFramework::EditorEntityIdContainer::GetMimeType(), encodedData);
  523. return mimeDataPtr;
  524. }
  525. bool HierarchyWidget::AcceptsMimeData(const QMimeData* mimeData)
  526. {
  527. if (!mimeData)
  528. {
  529. return false;
  530. }
  531. if (!m_isInited)
  532. {
  533. return false;
  534. }
  535. if (mimeData->hasFormat(AzToolsFramework::EditorEntityIdContainer::GetMimeType()))
  536. {
  537. QByteArray arrayData = mimeData->data(AzToolsFramework::EditorEntityIdContainer::GetMimeType());
  538. AzToolsFramework::EditorEntityIdContainer entityIdListContainer;
  539. if (!entityIdListContainer.FromBuffer(arrayData.constData(), arrayData.size()))
  540. {
  541. return false;
  542. }
  543. if (entityIdListContainer.m_entityIds.empty())
  544. {
  545. return false;
  546. }
  547. // Get the entity context that the first dragged entity is attached to
  548. AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull();
  549. AzFramework::EntityIdContextQueryBus::EventResult(
  550. contextId, entityIdListContainer.m_entityIds[0], &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId);
  551. if (contextId.IsNull())
  552. {
  553. return false;
  554. }
  555. // Check that the entity context is the UI editor entity context
  556. UiEditorEntityContext* editorEntityContext = m_editorWindow->GetEntityContext();
  557. if (!editorEntityContext || (editorEntityContext->GetContextId() != contextId))
  558. {
  559. return false;
  560. }
  561. return true;
  562. }
  563. return AssetDropHelpers::DoesMimeDataContainSliceOrComponentAssets(mimeData);
  564. }
  565. void HierarchyWidget::DropMimeDataAssetsAtHierarchyPosition(const QMimeData* mimeData, const QPoint& position)
  566. {
  567. using namespace AzToolsFramework;
  568. // Check where the drop indicator is to determine the parent for a new entity
  569. // or to determine an existing entity for new components
  570. QTreeWidgetItem* item = itemAt(position);
  571. DropIndicatorPosition dropPosition = dropIndicatorPosition();
  572. QTreeWidgetItem* targetWidgetItem = nullptr;
  573. bool onItem = false;
  574. int childIndex = -1;
  575. switch (dropPosition)
  576. {
  577. case QAbstractItemView::AboveItem:
  578. targetWidgetItem = item->parent();
  579. childIndex = (targetWidgetItem ? targetWidgetItem : invisibleRootItem())->indexOfChild(item);
  580. break;
  581. case QAbstractItemView::BelowItem:
  582. targetWidgetItem = item->parent();
  583. childIndex = (targetWidgetItem ? targetWidgetItem : invisibleRootItem())->indexOfChild(item) + 1;
  584. break;
  585. case QAbstractItemView::OnItem:
  586. targetWidgetItem = item;
  587. // Shift modifier enables creating a child entity from the asset
  588. onItem = !(QApplication::keyboardModifiers() & Qt::ShiftModifier);
  589. break;
  590. case QAbstractItemView::OnViewport:
  591. targetWidgetItem = nullptr;
  592. break;
  593. }
  594. DropMimeDataAssets(mimeData, targetWidgetItem, onItem, childIndex, nullptr);
  595. }
  596. void HierarchyWidget::DropMimeDataAssets(const QMimeData* mimeData,
  597. QTreeWidgetItem* targetWidgetItem,
  598. bool onElement,
  599. int childIndex,
  600. const QPoint* newElementPosition)
  601. {
  602. ComponentAssetHelpers::ComponentAssetPairs componentAssetPairs;
  603. AssetDropHelpers::AssetList sliceAssets;
  604. AssetDropHelpers::DecodeSliceAndComponentAssetsFromMimeData(mimeData, componentAssetPairs, sliceAssets);
  605. if (componentAssetPairs.empty() && sliceAssets.empty())
  606. {
  607. return;
  608. }
  609. // Change current selection so instantiated slices will be parented correctly
  610. if (targetWidgetItem)
  611. {
  612. SetUniqueSelectionHighlight(targetWidgetItem);
  613. }
  614. else
  615. {
  616. clearSelection();
  617. }
  618. // Instantiate dropped slices
  619. for (const AZ::Data::AssetId& sliceAssetId : sliceAssets)
  620. {
  621. // Instantiate slice under currently selected parent
  622. AZ::Vector2 viewportPosition(-1.0f, -1.0f); // indicates no viewport position specified
  623. if (newElementPosition)
  624. {
  625. viewportPosition = QtHelpers::QPointFToVector2(*newElementPosition);
  626. }
  627. GetEditorWindow()->GetSliceManager()->InstantiateSlice(sliceAssetId, viewportPosition, childIndex);
  628. }
  629. if (componentAssetPairs.empty())
  630. {
  631. return;
  632. }
  633. // Add components to the element being hovered or to a newly created element
  634. if (onElement)
  635. {
  636. // Add components to the existing target element which is now the selected element
  637. AZ_Assert(targetWidgetItem, "Must provide a target item when dropping component assets onto an element");
  638. // Make a list of the component types to be added
  639. AZStd::vector<AZ::TypeId> componentTypes;
  640. componentTypes.reserve(componentAssetPairs.size());
  641. for (const ComponentAssetHelpers::ComponentAssetPair& pair : componentAssetPairs)
  642. {
  643. componentTypes.push_back(pair.first);
  644. }
  645. ComponentHelpers::EntityComponentPair firstIncompatibleComponentType = AZStd::make_pair(AZ::EntityId(), AZ::Uuid::CreateNull());
  646. if (!ComponentHelpers::CanAddComponentsToSelectedEntities(componentTypes, &firstIncompatibleComponentType))
  647. {
  648. const AZ::EntityId& entityId = firstIncompatibleComponentType.first;
  649. const AZ::TypeId& componentTypeId = firstIncompatibleComponentType.second;
  650. HierarchyItem* targetItem = HierarchyItem::RttiCast(targetWidgetItem);
  651. AZStd::string entityName(targetItem->GetElement() ? targetItem->GetElement()->GetName() : "<unknown>");
  652. if (!entityId.IsValid() || componentTypeId.IsNull())
  653. {
  654. const AZStd::string message = AZStd::string::format("Failed to add components to target element \"%s\".", entityName.c_str());
  655. QMessageBox::warning(m_editorWindow, tr("Asset Drop"), QString(message.c_str()));
  656. }
  657. else
  658. {
  659. AZ::ComponentDescriptor* descriptor = nullptr;
  660. AZ::ComponentDescriptorBus::EventResult(descriptor, firstIncompatibleComponentType.second, &AZ::ComponentDescriptorBus::Events::GetDescriptor);
  661. AZStd::string componentName(descriptor ? descriptor->GetName() : "<unknown>");
  662. const AZStd::string message = AZStd::string::format("Failed to add components to target element \"%s\". Component \"%s\" is not compatible.", entityName.c_str(), componentName.c_str());
  663. QMessageBox::warning(m_editorWindow, tr("Asset Drop"), QString(message.c_str()));
  664. }
  665. return;
  666. }
  667. // Batch-add all the components
  668. ComponentHelpers::AddComponentsWithAssetToSelectedEntities(componentAssetPairs);
  669. }
  670. else
  671. {
  672. // Create a new element
  673. QTreeWidgetItemRawPtrQList parentItems;
  674. if (targetWidgetItem)
  675. {
  676. parentItems.append(targetWidgetItem);
  677. }
  678. CommandHierarchyItemCreate::Push(m_editorWindow->GetActiveStack(),
  679. this,
  680. parentItems,
  681. childIndex,
  682. [this, componentAssetPairs, newElementPosition](AZ::Entity* element)
  683. {
  684. if (element)
  685. {
  686. // Set the element's position
  687. if (newElementPosition)
  688. {
  689. EntityHelpers::MoveElementToGlobalPosition(element, *newElementPosition);
  690. }
  691. // Make a list of the component types to be added
  692. AZStd::vector<AZ::TypeId> componentTypes;
  693. componentTypes.reserve(componentAssetPairs.size());
  694. for (const ComponentAssetHelpers::ComponentAssetPair& pair : componentAssetPairs)
  695. {
  696. componentTypes.push_back(pair.first);
  697. }
  698. ComponentHelpers::EntityComponentPair firstIncompatibleComponentType = AZStd::make_pair(AZ::EntityId(), AZ::Uuid::CreateNull());
  699. if (!ComponentHelpers::CanAddComponentsToEntity(componentTypes, element->GetId(), &firstIncompatibleComponentType))
  700. {
  701. const AZ::TypeId& componentTypeId = firstIncompatibleComponentType.second;
  702. if (componentTypeId.IsNull())
  703. {
  704. QMessageBox::warning(m_editorWindow, tr("Asset Drop"), tr("Failed to add components to new element."));
  705. }
  706. else
  707. {
  708. AZ::ComponentDescriptor* descriptor = nullptr;
  709. AZ::ComponentDescriptorBus::EventResult(descriptor, firstIncompatibleComponentType.second, &AZ::ComponentDescriptorBus::Events::GetDescriptor);
  710. AZStd::string componentName(descriptor ? descriptor->GetName() : "<unknown>");
  711. const AZStd::string message = AZStd::string::format("Failed to add components to new element. Component \"%s\" is not compatible.", componentName.c_str());
  712. QMessageBox::warning(m_editorWindow, tr("Asset Drop"), QString(message.c_str()));
  713. }
  714. return;
  715. }
  716. // Batch-add all the components
  717. ComponentHelpers::AddComponentsWithAssetToEntity(componentAssetPairs, element->GetId());
  718. // Name the entity after the first asset
  719. const ComponentAssetHelpers::ComponentAssetPair& pair = componentAssetPairs.front();
  720. const AZ::Data::AssetId& assetId = pair.second;
  721. AZStd::string assetPath;
  722. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  723. assetPath, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetPathById, assetId);
  724. if (!assetPath.empty())
  725. {
  726. AZStd::string entityName;
  727. AzFramework::StringFunc::Path::GetFileName(assetPath.c_str(), entityName);
  728. // Find a unique name for the new element
  729. AZ::EntityId parentEntityId;
  730. UiElementBus::EventResult(parentEntityId, element->GetId(), &UiElementBus::Events::GetParentEntityId);
  731. AZStd::string uniqueName;
  732. UiCanvasBus::EventResult(
  733. uniqueName,
  734. GetEditorWindow()->GetCanvas(),
  735. &UiCanvasBus::Events::GetUniqueChildName,
  736. parentEntityId,
  737. entityName,
  738. nullptr);
  739. element->SetName(uniqueName);
  740. QTreeWidgetItem* item = HierarchyHelpers::ElementToItem(this, element, false);
  741. AZ_Assert(item, "Newly created element doesn't have a hierarchy item");
  742. item->setText(0, uniqueName.c_str());
  743. }
  744. }
  745. else
  746. {
  747. QMessageBox::warning(m_editorWindow, tr("Asset Drop"), tr("Failed to create a new element to add components to."));
  748. }
  749. });
  750. }
  751. }
  752. void HierarchyWidget::mouseMoveEvent(QMouseEvent* ev)
  753. {
  754. HierarchyItem* itemBeingHovered = HierarchyItem::RttiCast(itemAt(ev->pos()));
  755. if (itemBeingHovered)
  756. {
  757. // Hovering.
  758. if (m_itemBeingHovered)
  759. {
  760. if (itemBeingHovered == m_itemBeingHovered)
  761. {
  762. // Still hovering over the same item.
  763. // Nothing to do.
  764. }
  765. else
  766. {
  767. // Hover start over a different item.
  768. // Hover ends over the previous item.
  769. m_itemBeingHovered->SetMouseIsHovering(false);
  770. // Hover starts over the current item.
  771. m_itemBeingHovered = itemBeingHovered;
  772. m_itemBeingHovered->SetMouseIsHovering(true);
  773. }
  774. }
  775. else
  776. {
  777. // Hover start.
  778. m_itemBeingHovered = itemBeingHovered;
  779. m_itemBeingHovered->SetMouseIsHovering(true);
  780. }
  781. }
  782. else
  783. {
  784. // Not hovering.
  785. if (m_itemBeingHovered)
  786. {
  787. // Hover end.
  788. m_itemBeingHovered->SetMouseIsHovering(false);
  789. m_itemBeingHovered = nullptr;
  790. }
  791. else
  792. {
  793. // Still not hovering.
  794. // Nothing to do.
  795. }
  796. }
  797. QTreeWidget::mouseMoveEvent(ev);
  798. }
  799. void HierarchyWidget::mouseReleaseEvent(QMouseEvent* ev)
  800. {
  801. if (m_selectionChangedBeforeDrag)
  802. {
  803. // Signal a selection change on the mouse release
  804. SignalUserSelectionHasChanged(selectedItems());
  805. }
  806. QTreeWidget::mouseReleaseEvent(ev);
  807. // In pick mode, the user can click on an item and drag the mouse to change the current item.
  808. // In this case, a click event is not sent on a mouse release, so set the current item as the
  809. // picked item here
  810. if (m_inObjectPickMode)
  811. {
  812. // If there is a current item, set that as picked
  813. if (currentIndex() != QModelIndex()) // check for a valid index
  814. {
  815. QTreeWidgetItem* item = itemFromIndex(currentIndex());
  816. if (item)
  817. {
  818. PickItem(HierarchyItem::RttiCast(item));
  819. }
  820. }
  821. }
  822. }
  823. void HierarchyWidget::leaveEvent(QEvent* ev)
  824. {
  825. ClearItemBeingHovered();
  826. // If an item is dragged really fast outside the hierarchy, the startDrag event is called,
  827. // but the dragEnterEvent and dragLeaveEvent are replaced with the leaveEvent.
  828. // In this case, perform the dragLeaveEvent here
  829. if (m_inDragStartState)
  830. {
  831. if (m_selectionChangedBeforeDrag)
  832. {
  833. m_signalSelectionChange = false;
  834. // Set the current selection to the items that were selected before the drag
  835. clearSelection();
  836. for (auto i : m_beforeDragSelection)
  837. {
  838. i->setSelected(true);
  839. }
  840. m_signalSelectionChange = true;
  841. }
  842. m_inDragStartState = false;
  843. }
  844. QTreeWidget::leaveEvent(ev);
  845. }
  846. void HierarchyWidget::ClearItemBeingHovered()
  847. {
  848. if (!m_itemBeingHovered)
  849. {
  850. // Nothing to do.
  851. return;
  852. }
  853. m_itemBeingHovered->SetMouseIsHovering(false);
  854. m_itemBeingHovered = nullptr;
  855. }
  856. void HierarchyWidget::UpdateSliceInfo()
  857. {
  858. // Update the slice information (color, font, tooltip) for all elements.
  859. // As a simple way of going through all the HierarchyItem's we use the
  860. // EntityHelpers::EntityToHierarchyItemMap
  861. for (auto mapItem : m_entityItemMap)
  862. {
  863. mapItem.second->UpdateSliceInfo();
  864. }
  865. }
  866. void HierarchyWidget::DropMimeDataAssets(const QMimeData* mimeData,
  867. const AZ::EntityId& targetEntityId,
  868. bool onElement,
  869. int childIndex,
  870. const QPoint* newElementPosition)
  871. {
  872. if (!m_isInited)
  873. {
  874. return;
  875. }
  876. QTreeWidgetItem* targetWidgetItem = targetEntityId.IsValid() ? HierarchyHelpers::ElementToItem(this, targetEntityId, false) : nullptr;
  877. DropMimeDataAssets(mimeData, targetWidgetItem, onElement, childIndex, newElementPosition);
  878. }
  879. void HierarchyWidget::DeleteSelectedItems()
  880. {
  881. DeleteSelectedItems(selectedItems());
  882. }
  883. void HierarchyWidget::OnEntityPickModeStarted()
  884. {
  885. setDragEnabled(false);
  886. m_currentItemBeforePickMode = currentIndex();
  887. m_selectionModeBeforePickMode = selectionMode();
  888. setSelectionMode(QAbstractItemView::NoSelection);
  889. m_editTriggersBeforePickMode = editTriggers();
  890. setEditTriggers(QAbstractItemView::NoEditTriggers);
  891. setCursor(m_editorWindow->GetEntityPickerCursor());
  892. m_inObjectPickMode = true;
  893. }
  894. void HierarchyWidget::OnEntityPickModeStopped()
  895. {
  896. if (m_inObjectPickMode)
  897. {
  898. setCurrentIndex(m_currentItemBeforePickMode);
  899. setDragEnabled(true);
  900. setSelectionMode(m_selectionModeBeforePickMode);
  901. setEditTriggers(m_editTriggersBeforePickMode);
  902. setCursor(Qt::ArrowCursor);
  903. m_inObjectPickMode = false;
  904. }
  905. }
  906. void HierarchyWidget::EntityHighlightRequested([[maybe_unused]] AZ::EntityId entityId)
  907. {
  908. }
  909. void HierarchyWidget::EntityStrongHighlightRequested(AZ::EntityId entityId)
  910. {
  911. // Check if this entity is in the same entity context
  912. if (!IsEntityInEntityContext(entityId))
  913. {
  914. return;
  915. }
  916. QTreeWidgetItem* item = HierarchyHelpers::ElementToItem(this, entityId, false);
  917. if (!item)
  918. {
  919. return;
  920. }
  921. // Scrolling to the entity will make sure that it is visible.
  922. // This will automatically open parents
  923. scrollToItem(item);
  924. // Select the entity
  925. SetUniqueSelectionHighlight(item);
  926. }
  927. void HierarchyWidget::PickItem(HierarchyItem* item)
  928. {
  929. const AZ::EntityId entityId = item->GetEntityId();
  930. if (entityId.IsValid())
  931. {
  932. AzToolsFramework::EditorPickModeRequestBus::Broadcast(
  933. &AzToolsFramework::EditorPickModeRequests::PickModeSelectEntity, entityId);
  934. AzToolsFramework::EditorPickModeRequestBus::Broadcast(
  935. &AzToolsFramework::EditorPickModeRequests::StopEntityPickMode);
  936. }
  937. }
  938. bool HierarchyWidget::IsEntityInEntityContext(AZ::EntityId entityId)
  939. {
  940. AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull();
  941. AzFramework::EntityIdContextQueryBus::EventResult(
  942. contextId, entityId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId);
  943. if (!contextId.IsNull())
  944. {
  945. UiEditorEntityContext* editorEntityContext = m_editorWindow->GetEntityContext();
  946. if (editorEntityContext && editorEntityContext->GetContextId() == contextId)
  947. {
  948. return true;
  949. }
  950. }
  951. return false;
  952. }
  953. void HierarchyWidget::ReparentItems(const QTreeWidgetItemRawPtrList& baseParentItems,
  954. const HierarchyItemRawPtrList& childItems)
  955. {
  956. CommandHierarchyItemReparent::Push(m_editorWindow->GetActiveStack(),
  957. this,
  958. childItems,
  959. baseParentItems);
  960. }
  961. void HierarchyWidget::ToggleVisibility(HierarchyItem* hierarchyItem)
  962. {
  963. bool isItemVisible = true;
  964. AZ::EntityId itemEntityId = hierarchyItem->GetEntityId();
  965. UiEditorBus::EventResult(isItemVisible, itemEntityId, &UiEditorBus::Events::GetIsVisible);
  966. // There is one exception to toggling the visibility. If the clicked item has invisible ancestors,
  967. // then we make that item and all its ancestors visible regardless of the item's visibility
  968. // Make a list of items to modify
  969. HierarchyItemRawPtrList items;
  970. // Look for invisible ancestors
  971. AZ::EntityId parent;
  972. UiElementBus::EventResult(parent, itemEntityId, &UiElementBus::Events::GetParentEntityId);
  973. while (parent.IsValid())
  974. {
  975. bool isParentVisible = true;
  976. UiEditorBus::EventResult(isParentVisible, parent, &UiEditorBus::Events::GetIsVisible);
  977. if (!isParentVisible)
  978. {
  979. items.push_back(m_entityItemMap[parent]);
  980. }
  981. AZ::EntityId newParent = parent;
  982. parent.SetInvalid();
  983. UiElementBus::EventResult(parent, newParent, &UiElementBus::Events::GetParentEntityId);
  984. }
  985. bool makeVisible = items.size() > 0 ? true : !isItemVisible;
  986. // Add the item that was clicked
  987. if (makeVisible != isItemVisible)
  988. {
  989. items.push_back(m_entityItemMap[itemEntityId]);
  990. }
  991. CommandHierarchyItemToggleIsVisible::Push(m_editorWindow->GetActiveStack(),
  992. this,
  993. items);
  994. }
  995. void HierarchyWidget::DeleteSelectedItems(const QTreeWidgetItemRawPtrQList& selectedItems)
  996. {
  997. CommandHierarchyItemDelete::Push(m_editorWindow->GetActiveStack(),
  998. this,
  999. selectedItems);
  1000. // This ensures there's no "current item".
  1001. SetUniqueSelectionHighlight((QTreeWidgetItem*)nullptr);
  1002. // IMPORTANT: This is necessary to indirectly trigger detach()
  1003. // in the PropertiesWidget.
  1004. SetUserSelection(nullptr);
  1005. }
  1006. void HierarchyWidget::Cut()
  1007. {
  1008. QTreeWidgetItemRawPtrQList selection = selectedItems();
  1009. HierarchyClipboard::CopySelectedItemsToClipboard(this,
  1010. selection);
  1011. DeleteSelectedItems(selection);
  1012. }
  1013. void HierarchyWidget::Copy()
  1014. {
  1015. HierarchyClipboard::CopySelectedItemsToClipboard(this,
  1016. selectedItems());
  1017. }
  1018. void HierarchyWidget::PasteAsSibling()
  1019. {
  1020. HierarchyClipboard::CreateElementsFromClipboard(this,
  1021. selectedItems(),
  1022. false);
  1023. }
  1024. void HierarchyWidget::PasteAsChild()
  1025. {
  1026. HierarchyClipboard::CreateElementsFromClipboard(this,
  1027. selectedItems(),
  1028. true);
  1029. }
  1030. void HierarchyWidget::SetEditorOnlyForSelectedItems(bool editorOnly)
  1031. {
  1032. QTreeWidgetItemRawPtrQList selection = selectedItems();
  1033. if (!selection.empty())
  1034. {
  1035. SerializeHelpers::SerializedEntryList preChangeState;
  1036. HierarchyClipboard::BeginUndoableEntitiesChange(m_editorWindow, preChangeState);
  1037. for (auto item : selection)
  1038. {
  1039. HierarchyItem* i = HierarchyItem::RttiCast(item);
  1040. AzToolsFramework::EditorOnlyEntityComponentRequestBus::Event(i->GetEntityId(), &AzToolsFramework::EditorOnlyEntityComponentRequests::SetIsEditorOnlyEntity, editorOnly);
  1041. i->UpdateEditorOnlyInfo();
  1042. }
  1043. HierarchyClipboard::EndUndoableEntitiesChange(m_editorWindow, "editor only selection", preChangeState);
  1044. emit editorOnlyStateChangedOnSelectedElements();
  1045. }
  1046. }
  1047. void HierarchyWidget::AddElement(const QTreeWidgetItemRawPtrQList& selectedItems, const QPoint* optionalPos)
  1048. {
  1049. const int childIndex = -1;
  1050. CommandHierarchyItemCreate::Push(m_editorWindow->GetActiveStack(),
  1051. this,
  1052. selectedItems,
  1053. childIndex,
  1054. [this, optionalPos](AZ::Entity* element)
  1055. {
  1056. if (optionalPos)
  1057. {
  1058. // Convert position to render viewport coords
  1059. QPoint scaledPosition = *optionalPos * GetEditorWindow()->GetViewport()->WidgetToViewportFactor();
  1060. EntityHelpers::MoveElementToGlobalPosition(element, scaledPosition);
  1061. }
  1062. });
  1063. }
  1064. void HierarchyWidget::SetUniqueSelectionHighlight(QTreeWidgetItem* item)
  1065. {
  1066. // Stop object pick mode when an action explicitly wants to set the hierarchy's selected items
  1067. AzToolsFramework::EditorPickModeRequestBus::Broadcast(
  1068. &AzToolsFramework::EditorPickModeRequests::StopEntityPickMode);
  1069. clearSelection();
  1070. setCurrentIndex(indexFromItem(item));
  1071. }
  1072. void HierarchyWidget::SetUniqueSelectionHighlight(const AZ::Entity* element)
  1073. {
  1074. SetUniqueSelectionHighlight(HierarchyHelpers::ElementToItem(this, element, false));
  1075. }
  1076. #include <moc_HierarchyWidget.cpp>