not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

226 lines
7.8 KiB

  1. /**
  2. _enyo.Arranger_ is an <a href="#enyo.Layout">enyo.Layout</a> that considers
  3. one of the controls it lays out as active. The other controls are placed
  4. relative to the active control as makes sense for the layout.
  5. Arranger supports dynamic layouts, meaning it's possible to transition
  6. between its layouts via animation. Typically, arrangers should lay out
  7. controls using CSS transforms, since these are optimized for animation. To
  8. support this, the controls in an Arranger are absolutely positioned, and
  9. the Arranger kind has an `accelerated` property, which marks controls for
  10. CSS compositing. The default setting of "auto" ensures that this will occur
  11. if enabled by the platform.
  12. For more information, see the documentation on
  13. [Arrangers](https://github.com/enyojs/enyo/wiki/Arrangers)
  14. in the Enyo Developer Guide.
  15. */
  16. enyo.kind({
  17. name: "enyo.Arranger",
  18. kind: "Layout",
  19. layoutClass: "enyo-arranger",
  20. /**
  21. Sets controls being laid out to use CSS compositing. A setting of "auto"
  22. will mark controls for compositing if the platform supports it.
  23. */
  24. accelerated: "auto",
  25. //* Property of the drag event used to calculate the amount a drag moves the layout
  26. dragProp: "ddx",
  27. //* Property of the drag event used to calculate the direction of a drag
  28. dragDirectionProp: "xDirection",
  29. //* Property of the drag event used to calculate whether a drag should occur
  30. canDragProp: "horizontal",
  31. /**
  32. If set to true, transitions between non-adjacent arrangements will go
  33. through the intermediate arrangements. This is useful when direct
  34. transitions between arrangements would be visually jarring.
  35. */
  36. incrementalPoints: false,
  37. /**
  38. Called when removing an arranger (for example, when switching a Panels
  39. control to a different arrangerKind). Subclasses should implement this
  40. function to reset whatever properties they've changed on child controls.
  41. You *must* call the superclass implementation in your subclass's
  42. _destroy_ function.
  43. */
  44. destroy: function() {
  45. var c$ = this.container.getPanels();
  46. for (var i=0, c; c=c$[i]; i++) {
  47. c._arranger = null;
  48. }
  49. this.inherited(arguments);
  50. },
  51. //* @public
  52. /**
  53. Arranges the given array of controls (_inC_) in the layout specified by
  54. _inName_. When implementing this method, rather than apply styling
  55. directly to controls, call _arrangeControl(inControl, inArrangement)_
  56. with an _inArrangement_ object with styling settings. These will then be
  57. applied via the _flowControl(inControl, inArrangement)_ method.
  58. */
  59. arrange: function(inC, inName) {
  60. },
  61. /**
  62. Sizes the controls in the layout. This method is called only at reflow
  63. time. Note that sizing is separated from other layout done in the
  64. _arrange_ method because it is expensive and not suitable for dynamic
  65. layout.
  66. */
  67. size: function() {
  68. },
  69. /**
  70. Called when a layout transition begins. Implement this method to perform
  71. tasks that should only occur when a transition starts; for example, some
  72. controls could be shown or hidden. In addition, the _transitionPoints_
  73. array may be set on the container to dictate the named arrangments
  74. between which the transition occurs.
  75. */
  76. start: function() {
  77. var f = this.container.fromIndex, t = this.container.toIndex;
  78. var p$ = this.container.transitionPoints = [f];
  79. // optionally add a transition point for each index between from and to.
  80. if (this.incrementalPoints) {
  81. var d = Math.abs(t - f) - 2;
  82. var i = f;
  83. while (d >= 0) {
  84. i = i + (t < f ? -1 : 1);
  85. p$.push(i);
  86. d--;
  87. }
  88. }
  89. p$.push(this.container.toIndex);
  90. },
  91. /**
  92. Called when a layout transition completes. Implement this method to
  93. perform tasks that should only occur when a transition ends; for
  94. example, some controls could be shown or hidden.
  95. */
  96. finish: function() {
  97. },
  98. //* @protected
  99. canDragEvent: function(inEvent) {
  100. return inEvent[this.canDragProp];
  101. },
  102. calcDragDirection: function(inEvent) {
  103. return inEvent[this.dragDirectionProp];
  104. },
  105. calcDrag: function(inEvent) {
  106. return inEvent[this.dragProp];
  107. },
  108. drag: function(inDp, inAn, inA, inBn, inB) {
  109. var f = this.measureArrangementDelta(-inDp, inAn, inA, inBn, inB);
  110. return f;
  111. },
  112. measureArrangementDelta: function(inX, inI0, inA0, inI1, inA1) {
  113. var d = this.calcArrangementDifference(inI0, inA0, inI1, inA1);
  114. var s = d ? inX / Math.abs(d) : 0;
  115. s = s * (this.container.fromIndex > this.container.toIndex ? -1 : 1);
  116. //enyo.log("delta", s);
  117. return s;
  118. },
  119. //* @public
  120. /**
  121. Called when dragging the layout, this method returns the difference in
  122. pixels between the arrangement _inA0_ for layout setting _inI0_ and
  123. arrangement _inA1_ for layout setting _inI1_. This data is used to calculate
  124. the percentage that a drag should move the layout between two active states.
  125. */
  126. calcArrangementDifference: function(inI0, inA0, inI1, inA1) {
  127. },
  128. //* @protected
  129. _arrange: function(inIndex) {
  130. var c$ = this.getOrderedControls(inIndex);
  131. this.arrange(c$, inIndex);
  132. },
  133. arrangeControl: function(inControl, inArrangement) {
  134. inControl._arranger = enyo.mixin(inControl._arranger || {}, inArrangement);
  135. },
  136. flow: function() {
  137. this.c$ = [].concat(this.container.getPanels());
  138. this.controlsIndex = 0;
  139. for (var i=0, c$=this.container.getPanels(), c; c=c$[i]; i++) {
  140. enyo.dom.accelerate(c, this.accelerated);
  141. if (enyo.platform.safari) {
  142. // On Safari-desktop, sometimes having the panel's direct child set to accelerate isn't sufficient
  143. // this is most often the case with Lists contained inside another control, inside a Panels
  144. var grands=c.children;
  145. for (var j=0, kid; kid=grands[j]; j++) {
  146. enyo.dom.accelerate(kid, this.accelerated);
  147. }
  148. }
  149. }
  150. },
  151. reflow: function() {
  152. var cn = this.container.hasNode();
  153. this.containerBounds = cn ? {width: cn.clientWidth, height: cn.clientHeight} : {};
  154. this.size();
  155. },
  156. flowArrangement: function() {
  157. var a = this.container.arrangement;
  158. if (a) {
  159. for (var i=0, c$=this.container.getPanels(), c; c=c$[i]; i++) {
  160. this.flowControl(c, a[i]);
  161. }
  162. }
  163. },
  164. //* @public
  165. /**
  166. Lays out the control (_inControl_) according to the settings stored in
  167. the _inArrangment_ object. By default, _flowControl_ will apply settings
  168. of left, top, and opacity. This method should only be implemented to
  169. apply other settings made via _arrangeControl_.
  170. */
  171. flowControl: function(inControl, inArrangement) {
  172. enyo.Arranger.positionControl(inControl, inArrangement);
  173. var o = inArrangement.opacity;
  174. if (o != null) {
  175. enyo.Arranger.opacifyControl(inControl, o);
  176. }
  177. },
  178. //* @protected
  179. // Gets an array of controls arranged in state order.
  180. // note: optimization, dial around a single array.
  181. getOrderedControls: function(inIndex) {
  182. var whole = Math.floor(inIndex);
  183. var a = whole - this.controlsIndex;
  184. var sign = a > 0;
  185. var c$ = this.c$ || [];
  186. for (var i=0; i<Math.abs(a); i++) {
  187. if (sign) {
  188. c$.push(c$.shift());
  189. } else {
  190. c$.unshift(c$.pop());
  191. }
  192. }
  193. this.controlsIndex = whole;
  194. return c$;
  195. },
  196. statics: {
  197. // Positions a control via transform: translateX/Y if supported and falls back to left/top if not.
  198. positionControl: function(inControl, inBounds, inUnit) {
  199. var unit = inUnit || "px";
  200. if (!this.updating) {
  201. if (enyo.dom.canTransform() && !enyo.platform.android) {
  202. var l = inBounds.left, t = inBounds.top;
  203. var l = enyo.isString(l) ? l : l && (l + unit);
  204. var t = enyo.isString(t) ? t : t && (t + unit);
  205. enyo.dom.transform(inControl, {translateX: l || null, translateY: t || null});
  206. } else {
  207. inControl.setBounds(inBounds, inUnit);
  208. }
  209. }
  210. },
  211. opacifyControl: function(inControl, inOpacity) {
  212. var o = inOpacity;
  213. // FIXME: very high/low settings of opacity can cause a control to
  214. // blink so cap this here.
  215. o = o > .99 ? 1 : (o < .01 ? 0 : o);
  216. // note: we only care about ie8
  217. if (enyo.platform.ie < 9) {
  218. inControl.applyStyle("filter", "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + (o * 100) + ")");
  219. } else {
  220. inControl.applyStyle("opacity", o);
  221. }
  222. }
  223. }
  224. });