LayerStructure.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Linq;
  5. using PixiEditor.Helpers.Extensions;
  6. using PixiEditor.Models.DataHolders;
  7. namespace PixiEditor.Models.Layers
  8. {
  9. // Notice for further developemnt. Remember to expose only GroupData classes if you want to modify this LayerStructure groups
  10. // LayerStructure should figure out the GuidStructureItem from its internal data. This will ensure that data will
  11. // modify correctly.
  12. // You should pass GuidStructureItem for operating on protected and private methods for faster data manipulation.
  13. /// <summary>
  14. /// Class containing layer groups structure and methods to operate on it.
  15. /// </summary>
  16. public class LayerStructure
  17. {
  18. public event EventHandler LayerStructureChanged;
  19. public Document Owner { get; set; }
  20. public ObservableCollection<GuidStructureItem> Groups { get; set; }
  21. public static bool GroupContainsOnlyLayer(Layer layer, GuidStructureItem layerGroup)
  22. {
  23. return layerGroup != null && layerGroup.Subgroups.Count == 0 && layerGroup.StartLayerGuid == layer.LayerGuid && layerGroup.EndLayerGuid == layer.LayerGuid;
  24. }
  25. public GuidStructureItem GetGroupByGuid(Guid? groupGuid)
  26. {
  27. return GetGroupByGuid(groupGuid, Groups);
  28. }
  29. public GuidStructureItem GetGroupByLayer(Guid layerGuid)
  30. {
  31. return GetGroupByLayer(layerGuid, Groups);
  32. }
  33. public static ObservableCollection<GuidStructureItem> CloneGroups(ObservableCollection<GuidStructureItem> groups)
  34. {
  35. ObservableCollection<GuidStructureItem> outputGroups = new();
  36. foreach (var group in groups.ToArray())
  37. {
  38. outputGroups.Add(group.CloneGroup());
  39. }
  40. return outputGroups;
  41. }
  42. public ObservableCollection<GuidStructureItem> CloneGroups()
  43. {
  44. return CloneGroups(Groups);
  45. }
  46. // This will allow to add new group with multiple layers and groups at once. Not working well, todo fix
  47. /*public GuidStructureItem AddNewGroup(string groupName, IEnumerable<Layer> layers, Guid activeLayer)
  48. {
  49. var activeLayerParent = GetGroupByLayer(activeLayer);
  50. List<GuidStructureItem> sameLevelGroups = new List<GuidStructureItem>();
  51. var group = AddNewGroup(groupName, activeLayer);
  52. if (activeLayerParent == null)
  53. {
  54. sameLevelGroups.AddRange(Groups);
  55. }
  56. else
  57. {
  58. sameLevelGroups.AddRange(activeLayerParent.Subgroups);
  59. }
  60. sameLevelGroups.Remove(group);
  61. group.Subgroups = new ObservableCollection<GuidStructureItem>(sameLevelGroups);
  62. sameLevelGroups = new(sameLevelGroups.Where(x => IsChildOf(activeLayer, x)));
  63. Guid lastLayer = activeLayer;
  64. foreach (var layer in layers)
  65. {
  66. if (layer.LayerGuid == activeLayer)
  67. {
  68. continue;
  69. }
  70. Owner.MoveLayerInStructure(layer.LayerGuid, lastLayer, false);
  71. lastLayer = layer.LayerGuid;
  72. }
  73. return group;
  74. }*/
  75. public GuidStructureItem AddNewGroup(string groupName, Guid childLayer)
  76. {
  77. var parent = GetGroupByLayer(childLayer);
  78. GuidStructureItem group = new(groupName, childLayer);
  79. if (parent == null)
  80. {
  81. Groups.Add(group);
  82. }
  83. else
  84. {
  85. group.Parent = parent;
  86. parent.Subgroups.Add(group);
  87. }
  88. group.GroupsChanged += Group_GroupsChanged;
  89. LayerStructureChanged?.Invoke(this, EventArgs.Empty);
  90. return group;
  91. }
  92. #nullable enable
  93. public void MoveGroup(Guid groupGuid, Guid? parentGroupGuid, int newIndex)
  94. {
  95. var group = GetGroupByGuid(groupGuid);
  96. var parentGroup = GetGroupByGuid(parentGroupGuid);
  97. bool reverseOrder = true;
  98. int groupTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == group.EndLayerGuid));
  99. int groupBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == group.StartLayerGuid));
  100. int difference = newIndex - groupTopIndex;
  101. if (newIndex < groupTopIndex)
  102. {
  103. reverseOrder = false;
  104. difference = newIndex - groupBottomIndex;
  105. }
  106. if (difference == 0)
  107. {
  108. return;
  109. }
  110. PreMoveReassignBounds(parentGroup, group);
  111. List<Guid> layersInOrder = GetLayersInOrder(new GroupData(groupTopIndex, groupBottomIndex));
  112. MoveLayersInGroup(layersInOrder, difference, reverseOrder);
  113. LayerStructureChanged?.Invoke(this, EventArgs.Empty);
  114. }
  115. /// <summary>
  116. /// Checks if group is nested inside parent group.
  117. /// </summary>
  118. /// <param name="group">Group to check.</param>
  119. /// <param name="parent">Parent of that group.</param>
  120. /// <returns>True if group is nested inside parent, false if not.</returns>
  121. public bool IsChildOf(GuidStructureItem? group, GuidStructureItem parent)
  122. {
  123. if (group == null)
  124. {
  125. return false;
  126. }
  127. foreach (var subgroup in parent.Subgroups)
  128. {
  129. if (subgroup == group)
  130. {
  131. return true;
  132. }
  133. if (subgroup.Subgroups.Count > 0)
  134. {
  135. if (IsChildOf(group, subgroup))
  136. {
  137. return true;
  138. }
  139. }
  140. }
  141. return false;
  142. }
  143. public void PreMoveReassignBounds(GroupData parentGroup, Guid layer)
  144. {
  145. PreMoveReassignBounds(GetGroupByGuid(parentGroup.GroupGuid), layer);
  146. }
  147. protected void PreMoveReassignBounds(GuidStructureItem? parentGroup, Guid layer)
  148. {
  149. if (parentGroup != null)
  150. {
  151. Guid oldStart = parentGroup.StartLayerGuid;
  152. Guid oldEnd = parentGroup.EndLayerGuid;
  153. GuidStructureItem parentOfParent = parentGroup.Parent;
  154. if (parentGroup.Subgroups.Count == 0 && parentGroup.StartLayerGuid == layer && parentGroup.EndLayerGuid == layer)
  155. {
  156. RemoveGroup(parentGroup);
  157. }
  158. else
  159. {
  160. if (parentGroup.EndLayerGuid == layer)
  161. {
  162. parentGroup.EndLayerGuid = FindBoundLayer(parentGroup, layer, false);
  163. }
  164. if (parentGroup.StartLayerGuid == layer)
  165. {
  166. parentGroup.StartLayerGuid = FindBoundLayer(parentGroup, layer, true);
  167. }
  168. }
  169. if (parentOfParent != null)
  170. {
  171. ApplyBoundsToParents(parentOfParent, parentGroup, oldStart, oldEnd);
  172. }
  173. }
  174. }
  175. public void PreMoveReassignBounds(GroupData parentGroup, GroupData group)
  176. {
  177. PreMoveReassignBounds(GetGroupByGuid(parentGroup.GroupGuid), GetGroupByGuid(group.GroupGuid));
  178. }
  179. protected void PreMoveReassignBounds(GuidStructureItem? parentGroup, GuidStructureItem group)
  180. {
  181. if (parentGroup != null)
  182. {
  183. Guid oldStart = parentGroup.StartLayerGuid;
  184. Guid oldEnd = parentGroup.EndLayerGuid;
  185. if (parentGroup.Subgroups.Count == 1 && parentGroup.StartLayerGuid == group.StartLayerGuid && parentGroup.EndLayerGuid == group.EndLayerGuid)
  186. {
  187. RemoveGroup(parentGroup);
  188. }
  189. else
  190. {
  191. if (group.EndLayerGuid == parentGroup.EndLayerGuid)
  192. {
  193. parentGroup.EndLayerGuid = FindBoundLayer(parentGroup, group.StartLayerGuid, false);
  194. }
  195. if (group.StartLayerGuid == parentGroup.StartLayerGuid)
  196. {
  197. parentGroup.StartLayerGuid = FindBoundLayer(parentGroup, group.EndLayerGuid, true);
  198. }
  199. }
  200. if (parentGroup.Parent != null)
  201. {
  202. ApplyBoundsToParents(parentGroup.Parent, parentGroup, oldStart, oldEnd);
  203. }
  204. }
  205. }
  206. /// <summary>
  207. /// Checks if layer is nested inside parent group.
  208. /// </summary>
  209. /// <param name="layerGuid">Layer GUID to check.</param>
  210. /// <param name="parent">Parent of that group.</param>
  211. /// <returns>True if layer is nested inside parent, false if not.</returns>
  212. public bool IsChildOf(Guid layerGuid, GuidStructureItem parent)
  213. {
  214. var layerParent = GetGroupByLayer(layerGuid);
  215. if (layerParent == parent)
  216. {
  217. return true;
  218. }
  219. return IsChildOf(layerParent, parent);
  220. }
  221. public void PostMoveReassignBounds(GroupData parentGroup, Guid layerGuid)
  222. {
  223. PostMoveReassignBounds(GetGroupByGuid(parentGroup.GroupGuid), layerGuid);
  224. }
  225. protected void PostMoveReassignBounds(GuidStructureItem? parentGroup, Guid layerGuid)
  226. {
  227. if (parentGroup != null)
  228. {
  229. Guid? oldStart = parentGroup.StartLayerGuid;
  230. Guid? oldEnd = parentGroup.EndLayerGuid;
  231. int layerIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == layerGuid));
  232. int folderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentGroup.EndLayerGuid));
  233. int folderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentGroup.StartLayerGuid));
  234. int finalTopIndex = Math.Max(folderTopIndex, layerIndex);
  235. int finalBottomIndex = Math.Min(folderBottomIndex, layerIndex);
  236. Guid? topBoundLayer = FindBoundLayer(layerGuid, finalTopIndex, finalBottomIndex, false);
  237. Guid? bottomBoundLayer = FindBoundLayer(layerGuid, finalTopIndex, finalBottomIndex, true);
  238. if (topBoundLayer == parentGroup.EndLayerGuid)
  239. {
  240. parentGroup.EndLayerGuid = layerGuid;
  241. }
  242. if (bottomBoundLayer == parentGroup.StartLayerGuid)
  243. {
  244. parentGroup.StartLayerGuid = layerGuid;
  245. }
  246. if (parentGroup.Parent != null)
  247. {
  248. ApplyBoundsToParents(parentGroup.Parent, parentGroup, oldStart, oldEnd);
  249. }
  250. }
  251. }
  252. public void PostMoveReassignBounds(GroupData parentGroup, GroupData group)
  253. {
  254. PostMoveReassignBounds(GetGroupByGuid(parentGroup.GroupGuid), GetGroupByGuid(group.GroupGuid));
  255. }
  256. protected void PostMoveReassignBounds(GuidStructureItem? parentGroup, GuidStructureItem group)
  257. {
  258. if (parentGroup != null)
  259. {
  260. Guid oldStart = parentGroup.StartLayerGuid;
  261. Guid oldEnd = parentGroup.EndLayerGuid;
  262. int folderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == group.EndLayerGuid));
  263. int folderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == group.StartLayerGuid));
  264. int parentFolderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentGroup.EndLayerGuid));
  265. int parentFolderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentGroup.StartLayerGuid));
  266. int finalTopIndex = Math.Max(folderTopIndex, parentFolderTopIndex);
  267. int finalBottomIndex = Math.Min(folderBottomIndex, parentFolderBottomIndex);
  268. Guid topBoundLayer = FindBoundLayer(group.StartLayerGuid, finalTopIndex, finalBottomIndex, false);
  269. Guid bottomBoundLayer = FindBoundLayer(group.EndLayerGuid, finalTopIndex, finalBottomIndex, true);
  270. if (topBoundLayer == parentGroup.EndLayerGuid)
  271. {
  272. parentGroup.EndLayerGuid = group.EndLayerGuid;
  273. }
  274. if (bottomBoundLayer == parentGroup.StartLayerGuid)
  275. {
  276. parentGroup.StartLayerGuid = group.StartLayerGuid;
  277. }
  278. if (parentGroup.Parent != null)
  279. {
  280. ApplyBoundsToParents(parentGroup.Parent, parentGroup, oldStart, oldEnd);
  281. }
  282. }
  283. }
  284. public void AssignParent(Guid layer, Guid? parent)
  285. {
  286. AssignParent(layer, parent.HasValue ? GetGroupByGuid(parent) : null);
  287. }
  288. protected void AssignParent(Guid layer, GuidStructureItem? parent)
  289. {
  290. var currentParent = GetGroupByLayer(layer);
  291. if (currentParent != null)
  292. {
  293. PreMoveReassignBounds(currentParent, layer);
  294. }
  295. PostMoveReassignBounds(parent, layer);
  296. LayerStructureChanged?.Invoke(this, EventArgs.Empty);
  297. }
  298. public List<Guid> GetLayersInOrder(GroupData group)
  299. {
  300. List<Guid> layerGuids = new();
  301. int minIndex = group.BottomIndex;
  302. int maxIndex = group.TopIndex;
  303. for (int i = minIndex; i <= maxIndex; i++)
  304. {
  305. layerGuids.Add(Owner.Layers[i].LayerGuid);
  306. }
  307. return layerGuids;
  308. }
  309. /// <summary>
  310. /// Gets all layers inside group, including nested groups.
  311. /// </summary>
  312. /// <param name="group">Group to get layers from.</param>
  313. /// <returns>List of layer guids.</returns>
  314. public List<Guid> GetGroupLayerGuids(GuidStructureItem group)
  315. {
  316. Layer layerTop = Owner.Layers.First(x => x.LayerGuid == group.EndLayerGuid);
  317. Layer layerBottom = Owner.Layers.First(x => x.LayerGuid == group.StartLayerGuid);
  318. int indexTop = Owner.Layers.IndexOf(layerTop);
  319. int indexBottom = Owner.Layers.IndexOf(layerBottom);
  320. return GetLayersInOrder(new GroupData(indexTop, indexBottom));
  321. }
  322. /// <summary>
  323. /// Gets all layers inside group, including nested groups.
  324. /// </summary>
  325. /// <param name="group">Group to get layers from.</param>
  326. /// <returns>List of layers.</returns>
  327. public List<Layer> GetGroupLayers(GuidStructureItem group)
  328. {
  329. List<Layer> layers = new();
  330. var layerGuids = GetGroupLayerGuids(group);
  331. foreach (var layerGuid in layerGuids)
  332. {
  333. layers.Add(Owner.Layers.First(x => x.LayerGuid == layerGuid));
  334. }
  335. return layers;
  336. }
  337. private void Group_GroupsChanged(object sender, EventArgs e)
  338. {
  339. LayerStructureChanged?.Invoke(this, EventArgs.Empty);
  340. }
  341. private void RemoveGroup(GuidStructureItem parentFolder)
  342. {
  343. parentFolder.GroupsChanged -= Group_GroupsChanged;
  344. if (parentFolder.Parent == null)
  345. {
  346. Groups.Remove(parentFolder);
  347. }
  348. else
  349. {
  350. parentFolder.Parent.Subgroups.Remove(parentFolder);
  351. }
  352. LayerStructureChanged?.Invoke(this, EventArgs.Empty);
  353. }
  354. private void ApplyBoundsToParents(GuidStructureItem parent, GuidStructureItem group, Guid? oldStart, Guid? oldEnd)
  355. {
  356. Guid parentOldStart = parent.StartLayerGuid;
  357. Guid parentOldEnd = parent.EndLayerGuid;
  358. if (parent.Subgroups.Count == 0)
  359. {
  360. RemoveGroup(parent);
  361. }
  362. if (parent.StartLayerGuid == oldStart)
  363. {
  364. parent.StartLayerGuid = group.StartLayerGuid;
  365. }
  366. if (parent.EndLayerGuid == oldEnd)
  367. {
  368. parent.EndLayerGuid = group.EndLayerGuid;
  369. }
  370. if (parent.Parent != null)
  371. {
  372. ApplyBoundsToParents(parent.Parent, parent, parentOldStart, parentOldEnd);
  373. }
  374. }
  375. private Guid FindBoundLayer(Guid layerGuid, int parentFolderTopIndex, int parentFolderBottomIndex, bool above)
  376. {
  377. return GetNextLayerGuid(
  378. layerGuid,
  379. GetLayersInOrder(new GroupData(parentFolderTopIndex, parentFolderBottomIndex)),
  380. above);
  381. }
  382. private Guid FindBoundLayer(GuidStructureItem parentFolder, Guid layerGuid, bool above)
  383. {
  384. int parentFolderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentFolder.EndLayerGuid));
  385. int parentFolderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentFolder.StartLayerGuid));
  386. return FindBoundLayer(layerGuid, parentFolderTopIndex, parentFolderBottomIndex, above);
  387. }
  388. private static Guid GetNextLayerGuid(Guid layer, List<Guid> allLayers, bool above)
  389. {
  390. int indexOfLayer = allLayers.IndexOf(layer);
  391. int modifier = above ? 1 : -1;
  392. int newIndex = Math.Clamp(indexOfLayer + modifier, 0, allLayers.Count - 1);
  393. return allLayers[newIndex];
  394. }
  395. private void MoveLayersInGroup(List<Guid> layers, int moveBy, bool reverseOrder)
  396. {
  397. List<Guid> layerGuids = reverseOrder ? layers.Reverse<Guid>().ToList() : layers;
  398. for (int i = 0; i < layers.Count; i++)
  399. {
  400. Guid layerGuid = layerGuids[i];
  401. var layer = Owner.Layers.First(x => x.LayerGuid == layerGuid);
  402. int layerIndex = Owner.Layers.IndexOf(layer);
  403. Owner.Layers.Move(layerIndex, layerIndex + moveBy);
  404. }
  405. }
  406. #nullable disable
  407. private GuidStructureItem GetGroupByLayer(Guid layerGuid, IEnumerable<GuidStructureItem> groups)
  408. {
  409. foreach (var currentGroup in groups)
  410. {
  411. int topIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == currentGroup.EndLayerGuid));
  412. int bottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == currentGroup.StartLayerGuid));
  413. var layers = GetLayersInOrder(new GroupData(topIndex, bottomIndex));
  414. if (currentGroup.Subgroups.Count > 0)
  415. {
  416. var group = GetGroupByLayer(layerGuid, currentGroup.Subgroups);
  417. if (group != null)
  418. {
  419. return group;
  420. }
  421. }
  422. if (layers.Contains(layerGuid))
  423. {
  424. return currentGroup;
  425. }
  426. }
  427. return null;
  428. }
  429. private GuidStructureItem GetGroupByGuid(Guid? groupGuid, IEnumerable<GuidStructureItem> groups)
  430. {
  431. foreach (var group in groups)
  432. {
  433. if (group.GroupGuid == groupGuid)
  434. {
  435. return group;
  436. }
  437. if (group.Subgroups.Count > 0)
  438. {
  439. var guid = GetGroupByGuid(groupGuid, group.Subgroups);
  440. if (guid != null)
  441. {
  442. return guid;
  443. }
  444. }
  445. }
  446. return null;
  447. }
  448. public LayerStructure(ObservableCollection<GuidStructureItem> items, Document owner)
  449. {
  450. Groups = items;
  451. Owner = owner;
  452. }
  453. public LayerStructure(Document owner)
  454. {
  455. Groups = new ObservableCollection<GuidStructureItem>();
  456. Owner = owner;
  457. }
  458. }
  459. }