1 /****************************************************************************
  2  Copyright (c) 2013-2014 Chukong Technologies Inc.
  3 
  4  http://www.cocos2d-x.org
  5 
  6  Permission is hereby granted, free of charge, to any person obtaining a copy
  7  of this software and associated documentation files (the "Software"), to deal
  8  in the Software without restriction, including without limitation the rights
  9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  copies of the Software, and to permit persons to whom the Software is
 11  furnished to do so, subject to the following conditions:
 12 
 13  The above copyright notice and this permission notice shall be included in
 14  all copies or substantial portions of the Software.
 15 
 16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22  THE SOFTWARE.
 23  ****************************************************************************/
 24 
 25 /**
 26  * Gets the layout manager by ccui.Layout's layout type.
 27  * @param {Number} type
 28  * @returns {ccui.linearVerticalLayoutManager|ccui.linearHorizontalLayoutManager|ccui.relativeLayoutManager|null}
 29  */
 30 ccui.getLayoutManager = function (type) {
 31     switch (type) {
 32         case ccui.Layout.LINEAR_VERTICAL:
 33             return ccui.linearVerticalLayoutManager;
 34         case ccui.Layout.LINEAR_HORIZONTAL:
 35             return ccui.linearHorizontalLayoutManager;
 36         case ccui.Layout.RELATIVE:
 37             return ccui.relativeLayoutManager;
 38     }
 39     return null;
 40 };
 41 
 42 /**
 43  * ccui.linearVerticalLayoutManager is a singleton object which is the linear vertical layout manager for ccui.Layout.
 44  * @class
 45  * @name ccui.linearVerticalLayoutManager
 46  */
 47 ccui.linearVerticalLayoutManager = /** @lends ccui.linearVerticalLayoutManager# */{
 48     _doLayout: function(layout){
 49         var layoutSize = layout._getLayoutContentSize();
 50         var container = layout._getLayoutElements();
 51         var topBoundary = layoutSize.height;
 52 
 53         for (var i = 0, len = container.length; i < len; i++) {
 54             var child = container[i];
 55             if (child) {
 56                 var layoutParameter = child.getLayoutParameter();
 57 
 58                 if (layoutParameter){
 59                     var childGravity = layoutParameter.getGravity();
 60                     var ap = child.getAnchorPoint();
 61                     var cs = child.getContentSize();
 62                     var finalPosX = ap.x * cs.width;
 63                     var finalPosY = topBoundary - ((1.0-ap.y) * cs.height);
 64                     switch (childGravity){
 65                         case ccui.LinearLayoutParameter.NONE:
 66                         case ccui.LinearLayoutParameter.LEFT:
 67                             break;
 68                         case ccui.LinearLayoutParameter.RIGHT:
 69                             finalPosX = layoutSize.width - ((1.0 - ap.x) * cs.width);
 70                             break;
 71                         case ccui.LinearLayoutParameter.CENTER_HORIZONTAL:
 72                             finalPosX = layoutSize.width / 2.0 - cs.width * (0.5-ap.x);
 73                             break;
 74                         default:
 75                             break;
 76                     }
 77                     var mg = layoutParameter.getMargin();
 78                     finalPosX += mg.left;
 79                     finalPosY -= mg.top;
 80                     child.setPosition(finalPosX, finalPosY);
 81                     topBoundary = child.getPositionY() - child.getAnchorPoint().y * child.getContentSize().height - mg.bottom;
 82                 }
 83             }
 84         }
 85     }
 86 };
 87 
 88 /**
 89  * ccui.linearHorizontalLayoutManager is a singleton object which is the linear horizontal layout manager for ccui.Layout
 90  * @class
 91  * @name ccui.linearHorizontalLayoutManager
 92  */
 93 ccui.linearHorizontalLayoutManager = /** @lends ccui.linearHorizontalLayoutManager# */{
 94     _doLayout: function(layout){
 95         var layoutSize = layout._getLayoutContentSize();
 96         var container = layout._getLayoutElements();
 97         var leftBoundary = 0.0;
 98         for (var i = 0, len = container.length;  i < len; i++) {
 99             var child = container[i];
100             if (child) {
101                 var layoutParameter = child.getLayoutParameter();
102                 if (layoutParameter){
103                     var childGravity = layoutParameter.getGravity();
104                     var ap = child.getAnchorPoint();
105                     var cs = child.getSize();
106                     var finalPosX = leftBoundary + (ap.x * cs.width);
107                     var finalPosY = layoutSize.height - (1.0 - ap.y) * cs.height;
108                     switch (childGravity){
109                         case ccui.LinearLayoutParameter.NONE:
110                         case ccui.LinearLayoutParameter.TOP:
111                             break;
112                         case ccui.LinearLayoutParameter.BOTTOM:
113                             finalPosY = ap.y * cs.height;
114                             break;
115                         case ccui.LinearLayoutParameter.CENTER_VERTICAL:
116                             finalPosY = layoutSize.height / 2.0 - cs.height * (0.5 - ap.y);
117                             break;
118                         default:
119                             break;
120                     }
121                     var mg = layoutParameter.getMargin();
122                     finalPosX += mg.left;
123                     finalPosY -= mg.top;
124                     child.setPosition(finalPosX, finalPosY);
125                     leftBoundary = child.getRightBoundary() + mg.right;
126                 }
127             }
128         }
129     }
130 };
131 
132 /**
133  * ccui.relativeLayoutManager is the singleton object which is the relative layout manager for ccui.Layout, it has a _doLayout function to do layout.
134  * @class
135  * @name ccui.relativeLayoutManager
136  */
137 ccui.relativeLayoutManager = /** @lends ccui.relativeLayoutManager# */{
138     _unlayoutChildCount: 0,
139     _widgetChildren: [],
140     _widget: null,
141     _finalPositionX:0,
142     _finalPositionY:0,
143     _relativeWidgetLP:null,
144 
145     _doLayout: function(layout){
146         this._widgetChildren = this._getAllWidgets(layout);
147 
148         var locChildren = this._widgetChildren;
149         while (this._unlayoutChildCount > 0) {
150             for (var i = 0, len = locChildren.length;  i < len; i++) {
151                 this._widget = locChildren[i];
152 
153                 var layoutParameter = this._widget.getLayoutParameter();
154                 if (layoutParameter){
155                     if (layoutParameter._put)
156                         continue;
157 
158                     var ret = this._calculateFinalPositionWithRelativeWidget(layout);
159                     if (!ret)
160                         continue;
161 
162                     this._calculateFinalPositionWithRelativeAlign();
163 
164                     this._widget.setPosition(this._finalPositionX, this._finalPositionY);
165                     layoutParameter._put = true;
166                 }
167             }
168             this._unlayoutChildCount--;
169         }
170         this._widgetChildren.length = 0;
171     },
172 
173     _getAllWidgets: function(layout){
174         var container = layout._getLayoutElements();
175         var locWidgetChildren = this._widgetChildren;
176         locWidgetChildren.length = 0;
177         for (var i = 0, len = container.length; i < len; i++){
178             var child = container[i];
179             if (child) {
180                 var layoutParameter = child.getLayoutParameter();
181                 layoutParameter._put = false;
182                 this._unlayoutChildCount++;
183                 locWidgetChildren.push(child);
184             }
185         }
186         return locWidgetChildren;
187     },
188 
189     _getRelativeWidget: function(widget){
190         var relativeWidget = null;
191         var layoutParameter = widget.getLayoutParameter();
192         var relativeName = layoutParameter.getRelativeToWidgetName();
193 
194         if (relativeName && relativeName.length != 0) {
195             var locChildren =  this._widgetChildren;
196             for(var i = 0, len = locChildren.length;  i  < len; i++){
197                 var child = locChildren[i];
198                 if (child){
199                     var rlayoutParameter = child.getLayoutParameter();
200                     if (rlayoutParameter &&  rlayoutParameter.getRelativeName() == relativeName) {
201                         relativeWidget = child;
202                         this._relativeWidgetLP = rlayoutParameter;
203                         break;
204                     }
205                 }
206             }
207         }
208         return relativeWidget;
209     },
210 
211     _calculateFinalPositionWithRelativeWidget: function(layout){
212         var locWidget = this._widget;
213         var ap = locWidget.getAnchorPoint();
214         var cs = locWidget.getContentSize();
215 
216         this._finalPositionX = 0.0;
217         this._finalPositionY = 0.0;
218 
219         var relativeWidget = this._getRelativeWidget(locWidget);
220         var layoutParameter = locWidget.getLayoutParameter();
221         var align = layoutParameter.getAlign();
222         var layoutSize = layout._getLayoutContentSize();
223 
224         switch (align) {
225             case ccui.RelativeLayoutParameter.NONE:
226             case ccui.RelativeLayoutParameter.PARENT_TOP_LEFT:
227                 this._finalPositionX = ap.x * cs.width;
228                 this._finalPositionY = layoutSize.height - ((1.0 - ap.y) * cs.height);
229                 break;
230             case ccui.RelativeLayoutParameter.PARENT_TOP_CENTER_HORIZONTAL:
231                 this._finalPositionX = layoutSize.width * 0.5 - cs.width * (0.5 - ap.x);
232                 this._finalPositionY = layoutSize.height - ((1.0 - ap.y) * cs.height);
233                 break;
234             case ccui.RelativeLayoutParameter.PARENT_TOP_RIGHT:
235                 this._finalPositionX = layoutSize.width - ((1.0 - ap.x) * cs.width);
236                 this._finalPositionY = layoutSize.height - ((1.0 - ap.y) * cs.height);
237                 break;
238             case ccui.RelativeLayoutParameter.PARENT_LEFT_CENTER_VERTICAL:
239                 this._finalPositionX = ap.x * cs.width;
240                 this._finalPositionY = layoutSize.height * 0.5 - cs.height * (0.5 - ap.y);
241                 break;
242             case ccui.RelativeLayoutParameter.CENTER_IN_PARENT:
243                 this._finalPositionX = layoutSize.width * 0.5 - cs.width * (0.5 - ap.x);
244                 this._finalPositionY = layoutSize.height * 0.5 - cs.height * (0.5 - ap.y);
245                 break;
246             case ccui.RelativeLayoutParameter.PARENT_RIGHT_CENTER_VERTICAL:
247                 this._finalPositionX = layoutSize.width - ((1.0 - ap.x) * cs.width);
248                 this._finalPositionY = layoutSize.height * 0.5 - cs.height * (0.5 - ap.y);
249                 break;
250             case ccui.RelativeLayoutParameter.PARENT_LEFT_BOTTOM:
251                 this._finalPositionX = ap.x * cs.width;
252                 this._finalPositionY = ap.y * cs.height;
253                 break;
254             case ccui.RelativeLayoutParameter.PARENT_BOTTOM_CENTER_HORIZONTAL:
255                 this._finalPositionX = layoutSize.width * 0.5 - cs.width * (0.5 - ap.x);
256                 this._finalPositionY = ap.y * cs.height;
257                 break;
258             case ccui.RelativeLayoutParameter.PARENT_RIGHT_BOTTOM:
259                 this._finalPositionX = layoutSize.width - ((1.0 - ap.x) * cs.width);
260                 this._finalPositionY = ap.y * cs.height;
261                 break;
262 
263             case ccui.RelativeLayoutParameter.LOCATION_ABOVE_LEFTALIGN:
264                 if (relativeWidget){
265                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
266                         return false;
267                     this._finalPositionY = relativeWidget.getTopBoundary() + ap.y * cs.height;
268                     this._finalPositionX = relativeWidget.getLeftBoundary() + ap.x * cs.width;
269                 }
270                 break;
271             case ccui.RelativeLayoutParameter.LOCATION_ABOVE_CENTER:
272                 if (relativeWidget){
273                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
274                         return false;
275                     var rbs = relativeWidget.getContentSize();
276                     this._finalPositionY = relativeWidget.getTopBoundary() + ap.y * cs.height;
277                     this._finalPositionX = relativeWidget.getLeftBoundary() + rbs.width * 0.5 + ap.x * cs.width - cs.width * 0.5;
278                 }
279                 break;
280             case ccui.RelativeLayoutParameter.LOCATION_ABOVE_RIGHTALIGN:
281                 if (relativeWidget) {
282                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
283                         return false;
284                     this._finalPositionY = relativeWidget.getTopBoundary() + ap.y * cs.height;
285                     this._finalPositionX = relativeWidget.getRightBoundary() - (1.0 - ap.x) * cs.width;
286                 }
287                 break;
288             case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_TOPALIGN:
289                 if (relativeWidget){
290                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
291                         return false;
292                     this._finalPositionY = relativeWidget.getTopBoundary() - (1.0 - ap.y) * cs.height;
293                     this._finalPositionX = relativeWidget.getLeftBoundary() - (1.0 - ap.x) * cs.width;
294                 }
295                 break;
296             case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_CENTER:
297                 if (relativeWidget) {
298                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
299                         return false;
300                     var rbs = relativeWidget.getContentSize();
301                     this._finalPositionX = relativeWidget.getLeftBoundary() - (1.0 - ap.x) * cs.width;
302                     this._finalPositionY = relativeWidget.getBottomBoundary() + rbs.height * 0.5 + ap.y * cs.height - cs.height * 0.5;
303                 }
304                 break;
305             case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_BOTTOMALIGN:
306                 if (relativeWidget) {
307                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
308                         return false;
309                     this._finalPositionY = relativeWidget.getBottomBoundary() + ap.y * cs.height;
310                     this._finalPositionX = relativeWidget.getLeftBoundary() - (1.0 - ap.x) * cs.width;
311                 }
312                 break;
313             case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_TOPALIGN:
314                 if (relativeWidget){
315                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
316                         return false;
317                     this._finalPositionY = relativeWidget.getTopBoundary() - (1.0 - ap.y) * cs.height;
318                     this._finalPositionX = relativeWidget.getRightBoundary() + ap.x * cs.width;
319                 }
320                 break;
321             case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_CENTER:
322                 if (relativeWidget){
323                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
324                         return false;
325                     var rbs = relativeWidget.getContentSize();
326                     var locationRight = relativeWidget.getRightBoundary();
327                     this._finalPositionX = locationRight + ap.x * cs.width;
328                     this._finalPositionY = relativeWidget.getBottomBoundary() + rbs.height * 0.5 + ap.y * cs.height - cs.height * 0.5;
329                 }
330                 break;
331             case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_BOTTOMALIGN:
332                 if (relativeWidget){
333                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
334                         return false;
335                     this._finalPositionY = relativeWidget.getBottomBoundary() + ap.y * cs.height;
336                     this._finalPositionX = relativeWidget.getRightBoundary() + ap.x * cs.width;
337                 }
338                 break;
339             case ccui.RelativeLayoutParameter.LOCATION_BELOW_LEFTALIGN:
340                 if (relativeWidget){
341                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
342                         return false;
343                     this._finalPositionY =  relativeWidget.getBottomBoundary() - (1.0 - ap.y) * cs.height;
344                     this._finalPositionX = relativeWidget.getLeftBoundary() + ap.x * cs.width;
345                 }
346                 break;
347             case ccui.RelativeLayoutParameter.LOCATION_BELOW_CENTER:
348                 if (relativeWidget) {
349                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
350                         return false;
351                     var rbs = relativeWidget.getContentSize();
352                     this._finalPositionY = relativeWidget.getBottomBoundary() - (1.0 - ap.y) * cs.height;
353                     this._finalPositionX = relativeWidget.getLeftBoundary() + rbs.width * 0.5 + ap.x * cs.width - cs.width * 0.5;
354                 }
355                 break;
356             case ccui.RelativeLayoutParameter.LOCATION_BELOW_RIGHTALIGN:
357                 if (relativeWidget) {
358                     if (this._relativeWidgetLP && !this._relativeWidgetLP._put)
359                         return false;
360                     this._finalPositionY = relativeWidget.getBottomBoundary() - (1.0 - ap.y) * cs.height;
361                     this._finalPositionX = relativeWidget.getRightBoundary() - (1.0 - ap.x) * cs.width;
362                 }
363                 break;
364             default:
365                 break;
366         }
367         return true;
368     },
369 
370     _calculateFinalPositionWithRelativeAlign: function(){
371         var layoutParameter = this._widget.getLayoutParameter();
372 
373         var mg = layoutParameter.getMargin();
374         var align = layoutParameter.getAlign();
375 
376         //handle margin
377         switch (align) {
378             case ccui.RelativeLayoutParameter.NONE:
379             case ccui.RelativeLayoutParameter.PARENT_TOP_LEFT:
380                 this._finalPositionX += mg.left;
381                 this._finalPositionY -= mg.top;
382                 break;
383             case ccui.RelativeLayoutParameter.PARENT_TOP_CENTER_HORIZONTAL:
384                 this._finalPositionY -= mg.top;
385                 break;
386             case ccui.RelativeLayoutParameter.PARENT_TOP_RIGHT:
387                 this._finalPositionX -= mg.right;
388                 this._finalPositionY -= mg.top;
389                 break;
390             case ccui.RelativeLayoutParameter.PARENT_LEFT_CENTER_VERTICAL:
391                 this._finalPositionX += mg.left;
392                 break;
393             case ccui.RelativeLayoutParameter.CENTER_IN_PARENT:
394                 break;
395             case ccui.RelativeLayoutParameter.PARENT_RIGHT_CENTER_VERTICAL:
396                 this._finalPositionX -= mg.right;
397                 break;
398             case ccui.RelativeLayoutParameter.PARENT_LEFT_BOTTOM:
399                 this._finalPositionX += mg.left;
400                 this._finalPositionY += mg.bottom;
401                 break;
402             case ccui.RelativeLayoutParameter.PARENT_BOTTOM_CENTER_HORIZONTAL:
403                 this._finalPositionY += mg.bottom;
404                 break;
405             case ccui.RelativeLayoutParameter.PARENT_RIGHT_BOTTOM:
406                 this._finalPositionX -= mg.right;
407                 this._finalPositionY += mg.bottom;
408                 break;
409             case ccui.RelativeLayoutParameter.LOCATION_ABOVE_LEFTALIGN:
410                 this._finalPositionY += mg.bottom;
411                 this._finalPositionX += mg.left;
412                 break;
413             case ccui.RelativeLayoutParameter.LOCATION_ABOVE_RIGHTALIGN:
414                 this._finalPositionY += mg.bottom;
415                 this._finalPositionX -= mg.right;
416                 break;
417             case ccui.RelativeLayoutParameter.LOCATION_ABOVE_CENTER:
418                 this._finalPositionY += mg.bottom;
419                 break;
420             case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_TOPALIGN:
421                 this._finalPositionX -= mg.right;
422                 this._finalPositionY -= mg.top;
423                 break;
424             case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_BOTTOMALIGN:
425                 this._finalPositionX -= mg.right;
426                 this._finalPositionY += mg.bottom;
427                 break;
428             case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_CENTER:
429                 this._finalPositionX -= mg.right;
430                 break;
431             case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_TOPALIGN:
432                 this._finalPositionX += mg.left;
433                 this._finalPositionY -= mg.top;
434                 break;
435             case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_BOTTOMALIGN:
436                 this._finalPositionX += mg.left;
437                 this._finalPositionY += mg.bottom;
438                 break;
439             case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_CENTER:
440                 this._finalPositionX += mg.left;
441                 break;
442             case ccui.RelativeLayoutParameter.LOCATION_BELOW_LEFTALIGN:
443                 this._finalPositionY -= mg.top;
444                 this._finalPositionX += mg.left;
445                 break;
446             case ccui.RelativeLayoutParameter.LOCATION_BELOW_RIGHTALIGN:
447                 this._finalPositionY -= mg.top;
448                 this._finalPositionX -= mg.right;
449                 break;
450             case ccui.RelativeLayoutParameter.LOCATION_BELOW_CENTER:
451                 this._finalPositionY -= mg.top;
452                 break;
453             default:
454                 break;
455         }
456     }
457 };