SelectionHelpers.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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. namespace SelectionHelpers
  10. {
  11. //-------------------------------------------------------------------------------
  12. void UnmarkAllAndAllTheirChildren(QTreeWidgetItem* baseRootItem)
  13. {
  14. // Seed the list.
  15. HierarchyItemRawPtrList itemList;
  16. HierarchyHelpers::AppendAllChildrenToEndOfList(baseRootItem, itemList);
  17. // Traverse the list.
  18. HierarchyHelpers::TraverseListAndAllChildren(itemList,
  19. [](HierarchyItem* childItem)
  20. {
  21. childItem->SetMark(false);
  22. });
  23. }
  24. void MarkOnlyDirectChildrenOf(const QTreeWidgetItemRawPtrQList& parentItems)
  25. {
  26. for (auto j : parentItems)
  27. {
  28. int end = j->childCount();
  29. for (int i = 0; i < end; ++i)
  30. {
  31. HierarchyItem* item = HierarchyItem::RttiCast(j->child(i));
  32. item->SetMark(true);
  33. }
  34. }
  35. }
  36. bool A_IsParentOf_B(QTreeWidgetItem* A,
  37. QTreeWidgetItem* B)
  38. {
  39. // We MUST have an "A", and "A" and "B" MUST be different.
  40. // Otherwise, early-out. We don't need to test "B" here,
  41. // by itself, because we'll do so in the "while" loop below.
  42. if (!(A && (A != B)))
  43. {
  44. // Nothing to do.
  45. return false;
  46. }
  47. while (B)
  48. {
  49. // Walk up the hierarchy.
  50. B = B->parent();
  51. // If A is the parent of B,
  52. // A will eventually equal B.
  53. if (A == B)
  54. {
  55. return true;
  56. }
  57. }
  58. return false;
  59. }
  60. bool IsMarkedOrParentIsMarked(HierarchyItem* item)
  61. {
  62. if (!item)
  63. {
  64. // Nothing to do.
  65. return false;
  66. }
  67. do
  68. {
  69. if (item->GetMark())
  70. {
  71. return true;
  72. }
  73. // Walk up the hierarchy.
  74. item = item->Parent();
  75. }
  76. while (item);
  77. return false;
  78. }
  79. void FindUnmarked(HierarchyItemRawPtrList& results,
  80. const QTreeWidgetItemRawPtrQList& parentItems)
  81. {
  82. for (auto j : parentItems)
  83. {
  84. HierarchyItem* item = HierarchyItem::RttiCast(j);
  85. AZ_Assert(item, "There's an item in the Hierarchy that isn't a HierarchyItem.");
  86. if (!IsMarkedOrParentIsMarked(item))
  87. {
  88. results.push_back(item);
  89. }
  90. }
  91. }
  92. //-------------------------------------------------------------------------------
  93. AZ::Entity* GetTopLevelParentOfElement(const LyShine::EntityArray& elements, AZ::Entity* elementToFind)
  94. {
  95. do
  96. {
  97. for (auto e : elements)
  98. {
  99. if (e == elementToFind)
  100. {
  101. return e;
  102. }
  103. }
  104. elementToFind = EntityHelpers::GetParentElement(elementToFind);
  105. } while (elementToFind);
  106. return nullptr;
  107. }
  108. void RemoveEntityFromArray(LyShine::EntityArray& listToTrim, const AZ::Entity* entityToRemove)
  109. {
  110. for (auto e = listToTrim.begin(); e != listToTrim.end(); ++e)
  111. {
  112. if (*e == entityToRemove)
  113. {
  114. listToTrim.erase(e);
  115. return;
  116. }
  117. }
  118. }
  119. //-------------------------------------------------------------------------------
  120. void GetListOfTopLevelSelectedItems([[maybe_unused]] const HierarchyWidget* widget,
  121. const QTreeWidgetItemRawPtrQList& selectedItems,
  122. QTreeWidgetItemRawPtrQList& results)
  123. {
  124. AZ_Assert(&selectedItems != &results, "Input and output cannot be the same");
  125. results = selectedItems;
  126. // Remove all non-top-parent nodes.
  127. // IMPORTANT: This algorithm's time complexity is O(n^2),
  128. // and space complexity is O(n).
  129. for (auto i : selectedItems)
  130. {
  131. for (auto j : selectedItems)
  132. {
  133. if ((i != j) &&
  134. A_IsParentOf_B(i, j))
  135. {
  136. results.removeOne(j);
  137. }
  138. }
  139. }
  140. }
  141. void GetListOfTopLevelSelectedItems([[maybe_unused]] const HierarchyWidget* widget,
  142. const QTreeWidgetItemRawPtrQList& selectedItems,
  143. QTreeWidgetItem* invisibleRootItem,
  144. HierarchyItemRawPtrList& results)
  145. {
  146. UnmarkAllAndAllTheirChildren(invisibleRootItem);
  147. // Note: The mark is used as a pruning flag.
  148. // All items with a mark, or under a marked item, will be culled.
  149. MarkOnlyDirectChildrenOf(selectedItems);
  150. FindUnmarked(results, selectedItems);
  151. }
  152. //-------------------------------------------------------------------------------
  153. HierarchyItemRawPtrList GetSelectedHierarchyItems([[maybe_unused]] const HierarchyWidget* widget,
  154. const QTreeWidgetItemRawPtrQList& selectedItems)
  155. {
  156. HierarchyItemRawPtrList items;
  157. // selectedItems -> HierarchyItemRawPtrList.
  158. for (auto i : selectedItems)
  159. {
  160. HierarchyItem* item = HierarchyItem::RttiCast(i);
  161. if (item)
  162. {
  163. items.push_back(item);
  164. }
  165. else
  166. {
  167. AZ_Assert(0, "This should NEVER happen. Because we should ONLY be able to select HierarchyItem in the widget.");
  168. }
  169. }
  170. return items;
  171. }
  172. LyShine::EntityArray GetSelectedElements([[maybe_unused]] const HierarchyWidget* widget,
  173. const QTreeWidgetItemRawPtrQList& selectedItems)
  174. {
  175. auto count = selectedItems.count();
  176. LyShine::EntityArray elements(count);
  177. {
  178. for (int i = 0; i < count; ++i)
  179. {
  180. HierarchyItem* item = HierarchyItem::RttiCast(selectedItems[i]);
  181. if (item)
  182. {
  183. elements[i] = item->GetElement();
  184. }
  185. else
  186. {
  187. AZ_Assert(0, "This should NEVER happen, because every item in the hierarchy should represent an element.");
  188. }
  189. }
  190. }
  191. return elements;
  192. }
  193. EntityHelpers::EntityIdList GetSelectedElementIds([[maybe_unused]] const HierarchyWidget* widget,
  194. const QTreeWidgetItemRawPtrQList& selectedItems,
  195. bool addInvalidIdIfEmpty)
  196. {
  197. EntityHelpers::EntityIdList ids;
  198. {
  199. auto count = selectedItems.count();
  200. for (int i = 0; i < count; ++i)
  201. {
  202. HierarchyItem* item = HierarchyItem::RttiCast(selectedItems[i]);
  203. if (item)
  204. {
  205. ids.push_back(item->GetEntityId());
  206. }
  207. else
  208. {
  209. AZ_Assert(0, "This should NEVER happen, because every item in the hierarchy should represent an element.");
  210. }
  211. }
  212. if (addInvalidIdIfEmpty && ids.empty())
  213. {
  214. ids.push_back(AZ::EntityId());
  215. }
  216. }
  217. return ids;
  218. }
  219. LyShine::EntityArray GetTopLevelSelectedElements(const HierarchyWidget* widget,
  220. const QTreeWidgetItemRawPtrQList& selectedItems)
  221. {
  222. HierarchyItemRawPtrList topLevelSelectedItems;
  223. GetListOfTopLevelSelectedItems(widget,
  224. selectedItems,
  225. widget->invisibleRootItem(),
  226. topLevelSelectedItems);
  227. // HierarchyItemRawPtrList -> EntityArray.
  228. LyShine::EntityArray elements;
  229. for (auto item : topLevelSelectedItems)
  230. {
  231. elements.push_back(item->GetElement());
  232. }
  233. return elements;
  234. }
  235. LyShine::EntityArray GetTopLevelSelectedElementsNotControlledByParent(const HierarchyWidget* widget,
  236. const QTreeWidgetItemRawPtrQList& selectedItems)
  237. {
  238. HierarchyItemRawPtrList topLevelSelectedItems;
  239. GetListOfTopLevelSelectedItems(widget,
  240. selectedItems,
  241. widget->invisibleRootItem(),
  242. topLevelSelectedItems);
  243. // HierarchyItemRawPtrList -> EntityArray.
  244. LyShine::EntityArray elements;
  245. for (auto item : topLevelSelectedItems)
  246. {
  247. if (!ViewportHelpers::IsControlledByLayout(item->GetElement()))
  248. {
  249. elements.push_back(item->GetElement());
  250. }
  251. }
  252. return elements;
  253. }
  254. //-------------------------------------------------------------------------------
  255. } // namespace SelectionHelpers