Layers.hx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. package h2d;
  2. /**
  3. A layer-based container for Objects.
  4. Hierarchically organizes objects based on their layer.
  5. Supports per-layer Y-sorting through `Layers.ysort`.
  6. **/
  7. class Layers extends Object {
  8. // the per-layer insert position
  9. var layersIndexes : Array<Int>;
  10. var layerCount : Int;
  11. /**
  12. Create a new Layers instance.
  13. @param parent An optional parent `h2d.Object` instance to which Layers adds itself if set.
  14. **/
  15. public function new(?parent) {
  16. super(parent);
  17. layersIndexes = [];
  18. layerCount = 0;
  19. }
  20. /**
  21. Adds a child object `s` at the end of the topmost layer.
  22. @param s An object to be added.
  23. **/
  24. @:dox(show)
  25. override function addChild(s) {
  26. add(s, -1);
  27. }
  28. /**
  29. * Adds a child object `s` at the end of the given `layer`.
  30. * @param s An object to be added.
  31. * @param layer An index of the layer the object should be added at with 0 being the bottom-most layer. Pass -1 to use topmost layer.
  32. * @param index An optional index at which the object should be inserted inside the layer. Pass -1 to append to the end.
  33. */
  34. public function add( s : Object, layer : Int = -1, index : Int = -1) {
  35. if ( s.parent == this ) {
  36. // prevent calling onRemove
  37. var old = s.allocated;
  38. s.allocated = false;
  39. removeChild(s);
  40. s.allocated = old;
  41. }
  42. if ( layer == -1 ) layer = layerCount == 0 ? 0 : layerCount - 1;
  43. // Populate layer list
  44. while ( layer >= layerCount )
  45. layersIndexes[layerCount++] = children.length;
  46. if ( index != -1 ) {
  47. // Prevent inserting out of layer bounds.
  48. if ( layer == 0 )
  49. super.addChildAt(s, hxd.Math.imax(0, hxd.Math.imin(index, layersIndexes[layer])));
  50. else if ( index < 0 ) // clamp 0..
  51. super.addChildAt(s, layersIndexes[layer - 1]);
  52. else // clamp ..layerSize
  53. super.addChildAt(s, hxd.Math.imin(layersIndexes[layer - 1] + index, layersIndexes[layer]));
  54. } else {
  55. super.addChildAt(s, layersIndexes[layer]);
  56. }
  57. for ( i in layer...layerCount )
  58. layersIndexes[i]++;
  59. }
  60. /**
  61. Adds a child object `s` at specified `index` on the top topmost layer.
  62. Warning: Previous behavior of `Layers.addChildAt` is no longer applicable and `Layers.add` should be used instead.
  63. @param s The object to be added.
  64. @param index The position of the object in the layer.
  65. **/
  66. @:dox(show)
  67. override function addChildAt( s : Object, index : Int ) {
  68. add(s, -1, index);
  69. }
  70. override function removeChild( s : Object ) {
  71. // Full override due to child index being important for layer tracking.
  72. for( i in 0...children.length ) {
  73. if( children[i] == s ) {
  74. children.splice(i, 1);
  75. if( s.allocated ) s.onRemove();
  76. s.parent = null;
  77. s.posChanged = true;
  78. if( s.parentContainer != null ) s.setParentContainer(null);
  79. var k = layerCount - 1;
  80. while( k >= 0 && layersIndexes[k] > i ) {
  81. layersIndexes[k]--;
  82. k--;
  83. }
  84. #if domkit
  85. if( s.dom != null ) s.dom.onParentChanged();
  86. #end
  87. onContentChanged();
  88. break;
  89. }
  90. }
  91. }
  92. /**
  93. Moves an object `s` to the bottom of its layer (rendered first, behind the other Objects in the layer).
  94. Causes `Object.onHierarchyMoved` on the Object.
  95. @param s An object to be moved.
  96. */
  97. public function under( s : Object ) {
  98. for( i in 0...children.length )
  99. if( children[i] == s ) {
  100. var pos = 0;
  101. for( l in layersIndexes )
  102. if( l > i )
  103. break;
  104. else
  105. pos = l;
  106. var p = i;
  107. while( p > pos ) {
  108. children[p] = children[p - 1];
  109. p--;
  110. }
  111. children[pos] = s;
  112. // Force Interactive to reattach to scene in order to keep interaction order.
  113. if ( s.allocated )
  114. s.onHierarchyMoved(false);
  115. return;
  116. }
  117. }
  118. /**
  119. Moves an object `s` to the top of its layer (rendered last, in front of other Objects in layer).
  120. Causes `Object.onHierarchyMoved` on the Object.
  121. @param s An object to be moved.
  122. */
  123. public function over( s : Object ) {
  124. for( i in 0...children.length )
  125. if( children[i] == s ) {
  126. for( l in layersIndexes )
  127. if( l > i ) {
  128. for( p in i...l-1 )
  129. children[p] = children[p + 1];
  130. children[l - 1] = s;
  131. // Force Interactive to reattach to scene in order to keep interaction order.
  132. if ( s.allocated )
  133. s.onHierarchyMoved(false);
  134. return;
  135. }
  136. return;
  137. }
  138. }
  139. /**
  140. Returns an Iterator with objects in a specified `layer`.
  141. Returns an empty iterator if no objects are present in the layer.
  142. Objects added or removed from Layers during iteration do not affect the output of the Iterator.
  143. @param layer A layer index to iterate over.
  144. */
  145. public function getLayer( layer : Int ) : Iterator<Object> {
  146. var a;
  147. if( layer >= layerCount )
  148. a = [];
  149. else {
  150. var start = layer == 0 ? 0 : layersIndexes[layer - 1];
  151. var max = layersIndexes[layer];
  152. a = children.slice(start, max);
  153. }
  154. return new hxd.impl.ArrayIterator(a);
  155. }
  156. /**
  157. Return the `n`th element among the immediate children list on the `layer`, or null if there is none.
  158. @param layer The layer children of which are used. Pass -1 to use the topmost layer.
  159. **/
  160. public function getChildAtLayer( n : Int, layer : Int ) : Object {
  161. if ( layer == -1 ) layer = layerCount == 0 ? 0 : layerCount - 1;
  162. if ( layer >= layerCount || n < 0 || n >= layersIndexes[layer] ) return null;
  163. if ( layer == 0 ) return children[n];
  164. return children[layersIndexes[layer - 1] + n];
  165. }
  166. /**
  167. Returns the layer on which the child `s` resides on.
  168. @param s An object to look up to.
  169. @return An index of the layer where the object resides on or `-1` if `s` is not a child of the Layers.
  170. */
  171. public function getChildLayer( s : Object ) : Int {
  172. if ( s.parent != this ) return -1;
  173. var index = children.indexOf(s);
  174. for ( i in 0...layerCount )
  175. if ( layersIndexes[i] > index ) return i;
  176. return -1;
  177. }
  178. /**
  179. Return the index of the child within its respective layer.
  180. @param o The child to look up index of.
  181. @returns `-1` if object is not a child of Layers, index of the child within its current layer otherwise.
  182. **/
  183. public function getChildIndexInLayer( o : Object ):Int
  184. {
  185. if ( o.parent != this ) return -1;
  186. var index = children.indexOf(o);
  187. if ( index < layersIndexes[0] ) return index;
  188. for ( i in 1...layerCount )
  189. if ( layersIndexes[i] > index ) return index - layersIndexes[i - 1];
  190. return -1;
  191. }
  192. function drawLayer( ctx : RenderContext, layer : Int ) {
  193. if( layer >= layerCount )
  194. return;
  195. var old = ctx.globalAlpha;
  196. ctx.globalAlpha *= alpha;
  197. var start = layer == 0 ? 0 : layersIndexes[layer - 1];
  198. var max = layersIndexes[layer];
  199. if( ctx.front2back ) {
  200. for( i in start...max ) children[max - 1 - i].drawRec(ctx);
  201. } else {
  202. for( i in start...max ) children[i].drawRec(ctx);
  203. }
  204. ctx.globalAlpha = old;
  205. }
  206. /**
  207. Sorts specified layer based on `Object.y` value of it's children.
  208. Causes `Object.onHierarchyChanged` on moved children.
  209. @param layer An index of the layer to sort.
  210. */
  211. public function ysort( layer : Int ) {
  212. if( layer >= layerCount ) return;
  213. var start = layer == 0 ? 0 : layersIndexes[layer - 1];
  214. var max = layersIndexes[layer];
  215. if( start == max )
  216. return;
  217. var pos = start;
  218. var ymax = children[pos++].y;
  219. while( pos < max ) {
  220. var c = children[pos];
  221. if( c.y < ymax ) {
  222. var p = pos - 1;
  223. while( p >= start ) {
  224. var c2 = children[p];
  225. if( c.y >= c2.y ) break;
  226. children[p + 1] = c2;
  227. p--;
  228. }
  229. children[p + 1] = c;
  230. if ( c.allocated )
  231. c.onHierarchyMoved(false);
  232. } else
  233. ymax = c.y;
  234. pos++;
  235. }
  236. }
  237. }