dropPreview.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. #ifdef 0
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  4. * You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #endif
  6. /**
  7. * This singleton provides the ability to re-arrange the current grid to
  8. * indicate the transformation that results from dropping a cell at a certain
  9. * position.
  10. */
  11. var gDropPreview = {
  12. /**
  13. * Rearranges the sites currently contained in the grid when a site would be
  14. * dropped onto the given cell.
  15. * @param aCell The drop target cell.
  16. * @return The re-arranged array of sites.
  17. */
  18. rearrange: function(aCell) {
  19. let sites = gGrid.sites;
  20. // Insert the dragged site into the current grid.
  21. this._insertDraggedSite(sites, aCell);
  22. // After the new site has been inserted we need to correct the positions
  23. // of all pinned tabs that have been moved around.
  24. this._repositionPinnedSites(sites, aCell);
  25. return sites;
  26. },
  27. /**
  28. * Inserts the currently dragged site into the given array of sites.
  29. * @param aSites The array of sites to insert into.
  30. * @param aCell The drop target cell.
  31. */
  32. _insertDraggedSite: function(aSites, aCell) {
  33. let dropIndex = aCell.index;
  34. let draggedSite = gDrag.draggedSite;
  35. // We're currently dragging a site.
  36. if (draggedSite) {
  37. let dragCell = draggedSite.cell;
  38. let dragIndex = dragCell.index;
  39. // Move the dragged site into its new position.
  40. if (dragIndex != dropIndex) {
  41. aSites.splice(dragIndex, 1);
  42. aSites.splice(dropIndex, 0, draggedSite);
  43. }
  44. // We're handling an external drag item.
  45. } else {
  46. aSites.splice(dropIndex, 0, null);
  47. }
  48. },
  49. /**
  50. * Correct the position of all pinned sites that might have been moved to
  51. * different positions after the dragged site has been inserted.
  52. * @param aSites The array of sites containing the dragged site.
  53. * @param aCell The drop target cell.
  54. */
  55. _repositionPinnedSites:
  56. function(aSites, aCell) {
  57. // Collect all pinned sites.
  58. let pinnedSites = this._filterPinnedSites(aSites, aCell);
  59. // Correct pinned site positions.
  60. pinnedSites.forEach(function(aSite) {
  61. aSites[aSites.indexOf(aSite)] = aSites[aSite.cell.index];
  62. aSites[aSite.cell.index] = aSite;
  63. }, this);
  64. // There might be a pinned cell that got pushed out of the grid, try to
  65. // sneak it in by removing a lower-priority cell.
  66. if (this._hasOverflowedPinnedSite(aSites, aCell))
  67. this._repositionOverflowedPinnedSite(aSites, aCell);
  68. },
  69. /**
  70. * Filter pinned sites out of the grid that are still on their old positions
  71. * and have not moved.
  72. * @param aSites The array of sites to filter.
  73. * @param aCell The drop target cell.
  74. * @return The filtered array of sites.
  75. */
  76. _filterPinnedSites: function(aSites, aCell) {
  77. let draggedSite = gDrag.draggedSite;
  78. // When dropping on a cell that contains a pinned site make sure that all
  79. // pinned cells surrounding the drop target are moved as well.
  80. let range = this._getPinnedRange(aCell);
  81. return aSites.filter(function(aSite, aIndex) {
  82. // The site must be valid, pinned and not the dragged site.
  83. if (!aSite || aSite == draggedSite || !aSite.isPinned())
  84. return false;
  85. let index = aSite.cell.index;
  86. // If it's not in the 'pinned range' it's a valid pinned site.
  87. return (index > range.end || index < range.start);
  88. });
  89. },
  90. /**
  91. * Determines the range of pinned sites surrounding the drop target cell.
  92. * @param aCell The drop target cell.
  93. * @return The range of pinned cells.
  94. */
  95. _getPinnedRange: function(aCell) {
  96. let dropIndex = aCell.index;
  97. let range = {start: dropIndex, end: dropIndex};
  98. // We need a pinned range only when dropping on a pinned site.
  99. if (aCell.containsPinnedSite()) {
  100. let links = gPinnedLinks.links;
  101. // Find all previous siblings of the drop target that are pinned as well.
  102. while (range.start && links[range.start - 1])
  103. range.start--;
  104. let maxEnd = links.length - 1;
  105. // Find all next siblings of the drop target that are pinned as well.
  106. while (range.end < maxEnd && links[range.end + 1])
  107. range.end++;
  108. }
  109. return range;
  110. },
  111. /**
  112. * Checks if the given array of sites contains a pinned site that has
  113. * been pushed out of the grid.
  114. * @param aSites The array of sites to check.
  115. * @param aCell The drop target cell.
  116. * @return Whether there is an overflowed pinned cell.
  117. */
  118. _hasOverflowedPinnedSite:
  119. function(aSites, aCell) {
  120. // If the drop target isn't pinned there's no way a pinned site has been
  121. // pushed out of the grid so we can just exit here.
  122. if (!aCell.containsPinnedSite())
  123. return false;
  124. let cells = gGrid.cells;
  125. // No cells have been pushed out of the grid, nothing to do here.
  126. if (aSites.length <= cells.length)
  127. return false;
  128. let overflowedSite = aSites[cells.length];
  129. // Nothing to do if the site that got pushed out of the grid is not pinned.
  130. return (overflowedSite && overflowedSite.isPinned());
  131. },
  132. /**
  133. * We have a overflowed pinned site that we need to re-position so that it's
  134. * visible again. We try to find a lower-priority cell (empty or containing
  135. * an unpinned site) that we can move it to.
  136. * @param aSites The array of sites.
  137. * @param aCell The drop target cell.
  138. */
  139. _repositionOverflowedPinnedSite:
  140. function(aSites, aCell) {
  141. // Try to find a lower-priority cell (empty or containing an unpinned site).
  142. let index = this._indexOfLowerPrioritySite(aSites, aCell);
  143. if (index > -1) {
  144. let cells = gGrid.cells;
  145. let dropIndex = aCell.index;
  146. // Move all pinned cells to their new positions to let the overflowed
  147. // site fit into the grid.
  148. for (let i = index + 1, lastPosition = index; i < aSites.length; i++) {
  149. if (i != dropIndex) {
  150. aSites[lastPosition] = aSites[i];
  151. lastPosition = i;
  152. }
  153. }
  154. // Finally, remove the overflowed site from its previous position.
  155. aSites.splice(cells.length, 1);
  156. }
  157. },
  158. /**
  159. * Finds the index of the last cell that is empty or contains an unpinned
  160. * site. These are considered to be of a lower priority.
  161. * @param aSites The array of sites.
  162. * @param aCell The drop target cell.
  163. * @return The cell's index.
  164. */
  165. _indexOfLowerPrioritySite:
  166. function(aSites, aCell) {
  167. let cells = gGrid.cells;
  168. let dropIndex = aCell.index;
  169. // Search (beginning with the last site in the grid) for a site that is
  170. // empty or unpinned (an thus lower-priority) and can be pushed out of the
  171. // grid instead of the pinned site.
  172. for (let i = cells.length - 1; i >= 0; i--) {
  173. // The cell that is our drop target is not a good choice.
  174. if (i == dropIndex)
  175. continue;
  176. let site = aSites[i];
  177. // We can use the cell only if it's empty or the site is un-pinned.
  178. if (!site || !site.isPinned())
  179. return i;
  180. }
  181. return -1;
  182. }
  183. };