/**************************************************************************** Copyright (c) 2011-2012 cocos2d-x.org Copyright (c) 2013-2014 Chukong Technologies Inc. http://www.cocos2d-x.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ /** * ccui.Layout is the base class of ccui.PageView and ccui.ScrollView, it does layout by layout manager * and clips area by its _clippingStencil when clippingEnabled is true. * @class * @extends ccui.Widget * * @property {Boolean} clippingEnabled - Indicate whether clipping is enabled * @property {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} clippingType * @property {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} layoutType * */ ccui.Layout = ccui.Widget.extend(/** @lends ccui.Layout# */{ _clippingEnabled: false, _backGroundScale9Enabled: null, _backGroundImage: null, _backGroundImageFileName: null, _backGroundImageCapInsets: null, _colorType: null, _bgImageTexType: ccui.Widget.LOCAL_TEXTURE, _colorRender: null, _gradientRender: null, _color: null, _startColor: null, _endColor: null, _alongVector: null, _opacity: 255, _backGroundImageTextureSize: null, _layoutType: null, _doLayoutDirty: true, _clippingRectDirty: true, _clippingType: null, _clippingStencil: null, _handleScissor: false, _scissorRectDirty: false, _clippingRect: null, _clippingParent: null, _className: "Layout", _backGroundImageColor: null, _finalPositionX: 0, _finalPositionY: 0, //clipping _currentStencilEnabled: 0, _currentStencilWriteMask: 0, _currentStencilFunc: 0, _currentStencilRef:0, _currentStencilValueMask:0, _currentStencilFail:0, _currentStencilPassDepthFail:0, _currentStencilPassDepthPass:0, _currentDepthWriteMask:0, _currentAlphaTestEnabled:0, _currentAlphaTestFunc:0, _currentAlphaTestRef:0, _backGroundImageOpacity:0, _mask_layer_le: 0, _loopFocus: false, //whether enable loop focus or not __passFocusToChild: false, //on default, it will pass the focus to the next nearest widget _isFocusPassing:false, //when finding the next focused widget, use this variable to pass focus between layout & widget /** * Allocates and initializes an UILayout. * Constructor of ccui.Layout * @function * @example * // example * var uiLayout = new ccui.Layout(); */ ctor: function () { this._layoutType = ccui.Layout.ABSOLUTE; this._widgetType = ccui.Widget.TYPE_CONTAINER; this._clippingType = ccui.Layout.CLIPPING_STENCIL; this._colorType = ccui.Layout.BG_COLOR_NONE; ccui.Widget.prototype.ctor.call(this); this._backGroundImageCapInsets = cc.rect(0, 0, 0, 0); this._color = cc.color(255, 255, 255, 255); this._startColor = cc.color(255, 255, 255, 255); this._endColor = cc.color(255, 255, 255, 255); this._alongVector = cc.p(0, -1); this._backGroundImageTextureSize = cc.size(0, 0); this._clippingRect = cc.rect(0, 0, 0, 0); this._backGroundImageColor = cc.color(255, 255, 255, 255); }, /** * Calls its parent's onEnter, and calls its clippingStencil's onEnter if clippingStencil isn't null. * @override */ onEnter: function(){ ccui.Widget.prototype.onEnter.call(this); if (this._clippingStencil) this._clippingStencil.onEnter(); this._doLayoutDirty = true; this._clippingRectDirty = true; }, /** * Calls its parent's onExit, and calls its clippingStencil's onExit if clippingStencil isn't null. * @override */ onExit: function(){ ccui.Widget.prototype.onExit.call(this); if (this._clippingStencil) this._clippingStencil.onExit(); }, /** * If a layout is loop focused which means that the focus movement will be inside the layout * @param {Boolean} loop pass true to let the focus movement loop inside the layout */ setLoopFocus: function(loop){ this._loopFocus = loop; }, /** * Gets whether enable focus loop * @returns {boolean} If focus loop is enabled, then it will return true, otherwise it returns false. The default value is false. */ isLoopFocus: function(){ return this._loopFocus; }, /** * Specifies whether the layout pass its focus to its child * @param pass To specify whether the layout pass its focus to its child */ setPassFocusToChild: function(pass){ this.__passFocusToChild = pass; }, /** * Returns whether the layout will pass the focus to its children or not. The default value is true * @returns {boolean} To query whether the layout will pass the focus to its children or not. The default value is true */ isPassFocusToChild: function(){ return this.__passFocusToChild; }, /** * When a widget is in a layout, you could call this method to get the next focused widget within a specified direction. * If the widget is not in a layout, it will return itself * @param {Number} direction the direction to look for the next focused widget in a layout * @param {ccui.Widget} current the current focused widget * @returns {ccui.Widget} return the index of widget in the layout */ findNextFocusedWidget: function(direction, current){ if (this._isFocusPassing || this.isFocused()) { var parent = this.getParent(); this._isFocusPassing = false; if (this.__passFocusToChild) { var w = this._passFocusToChild(direction, current); if (w instanceof ccui.Layout && parent) { parent._isFocusPassing = true; return parent.findNextFocusedWidget(direction, this); } return w; } if (null == parent) return this; parent._isFocusPassing = true; return parent.findNextFocusedWidget(direction, this); } else if(current.isFocused() || current instanceof ccui.Layout) { if (this._layoutType == ccui.Layout.LINEAR_HORIZONTAL) { switch (direction){ case ccui.Widget.LEFT: return this._getPreviousFocusedWidget(direction, current); break; case ccui.Widget.RIGHT: return this._getNextFocusedWidget(direction, current); break; case ccui.Widget.DOWN: case ccui.Widget.UP: if (this._isLastWidgetInContainer(this, direction)){ if (this._isWidgetAncestorSupportLoopFocus(current, direction)) return this.findNextFocusedWidget(direction, this); return current; } else { return this.findNextFocusedWidget(direction, this); } break; default: cc.assert(0, "Invalid Focus Direction"); return current; } } else if (this._layoutType == ccui.Layout.LINEAR_VERTICAL) { switch (direction){ case ccui.Widget.LEFT: case ccui.Widget.RIGHT: if (this._isLastWidgetInContainer(this, direction)) { if (this._isWidgetAncestorSupportLoopFocus(current, direction)) return this.findNextFocusedWidget(direction, this); return current; } else return this.findNextFocusedWidget(direction, this); break; case ccui.Widget.DOWN: return this._getNextFocusedWidget(direction, current); break; case ccui.Widget.UP: return this._getPreviousFocusedWidget(direction, current); break; default: cc.assert(0, "Invalid Focus Direction"); return current; } } else { cc.assert(0, "Un Supported Layout type, please use VBox and HBox instead!!!"); return current; } } else return current; }, /** * To specify a user-defined functor to decide which child widget of the layout should get focused * @function * @param {Number} direction * @param {ccui.Widget} current */ onPassFocusToChild: null, /** * override "init" method of widget. please do not call this function by yourself, you should pass the parameters to constructor to initialize it. * @returns {boolean} * @override */ init: function () { if (ccui.Widget.prototype.init.call(this)) { this.ignoreContentAdaptWithSize(false); this.setContentSize(cc.size(0, 0)); this.setAnchorPoint(0, 0); this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this); return true; } return false; }, __stencilDraw: function(ctx){ //Only for Canvas var locContext = ctx || cc._renderContext; var stencil = this._clippingStencil; var locEGL_ScaleX = cc.view.getScaleX(), locEGL_ScaleY = cc.view.getScaleY(); for (var i = 0; i < stencil._buffer.length; i++) { var element = stencil._buffer[i]; var vertices = element.verts; var firstPoint = vertices[0]; locContext.beginPath(); locContext.moveTo(firstPoint.x * locEGL_ScaleX, -firstPoint.y * locEGL_ScaleY); for (var j = 1, len = vertices.length; j < len; j++) locContext.lineTo(vertices[j].x * locEGL_ScaleX, -vertices[j].y * locEGL_ScaleY); } }, /** * Adds a widget to the container. * @param {ccui.Widget} widget * @param {Number} [zOrder] * @param {Number|string} [tag] tag or name * @override */ addChild: function (widget, zOrder, tag) { if ((widget instanceof ccui.Widget)) { this._supplyTheLayoutParameterLackToChild(widget); } ccui.Widget.prototype.addChild.call(this, widget, zOrder, tag); this._doLayoutDirty = true; }, /** * Removes child widget from ccui.Layout, and sets the layout dirty flag to true. * @param {ccui.Widget} widget * @param {Boolean} [cleanup=true] * @override */ removeChild: function (widget, cleanup) { ccui.Widget.prototype.removeChild.call(this, widget, cleanup); this._doLayoutDirty = true; }, /** * Removes all children from the container with a cleanup, and sets the layout dirty flag to true. * @param {Boolean} cleanup */ removeAllChildren: function (cleanup) { ccui.Widget.prototype.removeAllChildren.call(this, cleanup); this._doLayoutDirty = true; }, /** * Removes all children from the container, do a cleanup to all running actions depending on the cleanup parameter, * and sets the layout dirty flag to true. * @param {Boolean} cleanup true if all running actions on all children nodes should be cleanup, false otherwise. */ removeAllChildrenWithCleanup: function(cleanup){ ccui.Widget.prototype.removeAllChildrenWithCleanup.call(this, cleanup); this._doLayoutDirty = true; }, /** * Gets if layout is clipping enabled. * @returns {Boolean} if layout is clipping enabled. */ isClippingEnabled: function () { return this._clippingEnabled; }, /** *
* Calls adaptRenderers (its subclass will override it.) and do layout. * If clippingEnabled is true, it will clip/scissor area. *
* @override * @param {CanvasRenderingContext2D|WebGLRenderingContext} ctx */ visit: function (ctx) { if (!this._visible) return; this._adaptRenderers(); this._doLayout(); if (this._clippingEnabled) { switch (this._clippingType) { case ccui.Layout.CLIPPING_STENCIL: this._stencilClippingVisit(ctx); break; case ccui.Layout.CLIPPING_SCISSOR: this._scissorClippingVisit(ctx); break; default: break; } } else ccui.Widget.prototype.visit.call(this, ctx); }, _stencilClippingVisit: null, _stencilClippingVisitForWebGL: function (ctx) { var gl = ctx || cc._renderContext; if (!this._clippingStencil || !this._clippingStencil.isVisible()) return; // all the _stencilBits are in use? if (ccui.Layout._layer + 1 == cc.stencilBits) { // warn once ccui.Layout._visit_once = true; if (ccui.Layout._visit_once) { cc.log("Nesting more than " + cc.stencilBits + "stencils is not supported. Everything will be drawn without stencil for this node and its childs."); ccui.Layout._visit_once = false; } // draw everything, as if there where no stencil cc.Node.prototype.visit.call(this, ctx); return; } ccui.Layout._layer++; var mask_layer = 0x1 << ccui.Layout._layer; var mask_layer_l = mask_layer - 1; var mask_layer_le = mask_layer | mask_layer_l; // manually save the stencil state var currentStencilEnabled = gl.isEnabled(gl.STENCIL_TEST); var currentStencilWriteMask = gl.getParameter(gl.STENCIL_WRITEMASK); var currentStencilFunc = gl.getParameter(gl.STENCIL_FUNC); var currentStencilRef = gl.getParameter(gl.STENCIL_REF); var currentStencilValueMask = gl.getParameter(gl.STENCIL_VALUE_MASK); var currentStencilFail = gl.getParameter(gl.STENCIL_FAIL); var currentStencilPassDepthFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL); var currentStencilPassDepthPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS); gl.enable(gl.STENCIL_TEST); gl.stencilMask(mask_layer); var currentDepthWriteMask = gl.getParameter(gl.DEPTH_WRITEMASK); gl.depthMask(false); gl.stencilFunc(gl.NEVER, mask_layer, mask_layer); gl.stencilOp(gl.ZERO, gl.KEEP, gl.KEEP); // draw a fullscreen solid rectangle to clear the stencil buffer cc.kmGLMatrixMode(cc.KM_GL_PROJECTION); cc.kmGLPushMatrix(); cc.kmGLLoadIdentity(); cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); cc.kmGLPushMatrix(); cc.kmGLLoadIdentity(); cc._drawingUtil.drawSolidRect(cc.p(-1,-1), cc.p(1,1), cc.color(255, 255, 255, 255)); cc.kmGLMatrixMode(cc.KM_GL_PROJECTION); cc.kmGLPopMatrix(); cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); cc.kmGLPopMatrix(); gl.stencilFunc(gl.NEVER, mask_layer, mask_layer); gl.stencilOp(gl.REPLACE, gl.KEEP, gl.KEEP); cc.kmGLPushMatrix(); this.transform(); this._clippingStencil.visit(); gl.depthMask(currentDepthWriteMask); gl.stencilFunc(gl.EQUAL, mask_layer_le, mask_layer_le); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); // draw (according to the stencil test func) this node and its childs var i = 0; // used by _children var j = 0; // used by _protectedChildren this.sortAllChildren(); this.sortAllProtectedChildren(); var locChildren = this._children, locProtectChildren = this._protectedChildren; var iLen = locChildren.length, jLen = locProtectChildren.length, child; for( ; i < iLen; i++ ){ child = locChildren[i]; if ( child && child.getLocalZOrder() < 0 ) child.visit(); else break; } for( ; j < jLen; j++ ) { child = locProtectChildren[j]; if ( child && child.getLocalZOrder() < 0 ) child.visit(); else break; } this.draw(); for (; i < iLen; i++) locChildren[i].visit(); for (; j < jLen; j++) locProtectChildren[j].visit(); // manually restore the stencil state gl.stencilFunc(currentStencilFunc, currentStencilRef, currentStencilValueMask); gl.stencilOp(currentStencilFail, currentStencilPassDepthFail, currentStencilPassDepthPass); gl.stencilMask(currentStencilWriteMask); if (!currentStencilEnabled) gl.disable(gl.STENCIL_TEST); ccui.Layout._layer--; cc.kmGLPopMatrix(); }, _stencilClippingVisitForCanvas: function (ctx) { // return fast (draw nothing, or draw everything if in inverted mode) if: // - nil stencil node // - or stencil node invisible: if (!this._clippingStencil || !this._clippingStencil.isVisible()) { return; } var context = ctx || cc._renderContext; // Composition mode, costy but support texture stencil if (this._clippingStencil instanceof cc.Sprite) { // Cache the current canvas, for later use (This is a little bit heavy, replace this solution with other walkthrough) var canvas = context.canvas; var locCache = ccui.Layout._getSharedCache(); locCache.width = canvas.width; locCache.height = canvas.height; var locCacheCtx = locCache.getContext("2d"); locCacheCtx.drawImage(canvas, 0, 0); context.save(); // Draw everything first using node visit function cc.ProtectedNode.prototype.visit.call(this, context); context.globalCompositeOperation = "destination-in"; this.transform(context); this._clippingStencil.visit(); context.restore(); // Redraw the cached canvas, so that the cliped area shows the background etc. context.save(); context.setTransform(1, 0, 0, 1, 0, 0); context.globalCompositeOperation = "destination-over"; context.drawImage(locCache, 0, 0); context.restore(); } else { // Clip mode, fast, but only support cc.DrawNode var i, children = this._children, locChild; context.save(); this.transform(context); this._clippingStencil.visit(context); context.clip(); // Clip mode doesn't support recusive stencil, so once we used a clip stencil, // so if it has ClippingNode as a child, the child must uses composition stencil. this.sortAllChildren(); this.sortAllProtectedChildren(); var j, locProtectChildren = this._protectedChildren; var iLen = children.length, jLen = locProtectChildren.length; // draw children zOrder < 0 for (i = 0; i < iLen; i++) { locChild = children[i]; if (locChild && locChild._localZOrder < 0) locChild.visit(context); else break; } for (j = 0; j < jLen; j++) { locChild = locProtectChildren[j]; if (locChild && locChild._localZOrder < 0) locChild.visit(context); else break; } //this.draw(context); for (; i < iLen; i++) children[i].visit(context); for (; j < jLen; j++) locProtectChildren[j].visit(context); context.restore(); } }, _scissorClippingVisit: null, _scissorClippingVisitForWebGL: function (ctx) { var clippingRect = this._getClippingRect(); var gl = ctx || cc._renderContext; if (this._handleScissor) { gl.enable(gl.SCISSOR_TEST); } cc.view.setScissorInPoints(clippingRect.x, clippingRect.y, clippingRect.width, clippingRect.height); cc.Node.prototype.visit.call(this); if (this._handleScissor) { gl.disable(gl.SCISSOR_TEST); } }, /** * Changes if layout can clip it's content and locChild. * If you really need this, please enable it. But it would reduce the rendering efficiency. * @param {Boolean} able clipping enabled. */ setClippingEnabled: function (able) { if (able == this._clippingEnabled) return; this._clippingEnabled = able; switch (this._clippingType) { case ccui.Layout.CLIPPING_STENCIL: if (able){ this._clippingStencil = cc.DrawNode.create(); if(cc._renderType === cc._RENDER_TYPE_CANVAS) this._clippingStencil.draw = this.__stencilDraw.bind(this); if (this._running) this._clippingStencil.onEnter(); this._setStencilClippingSize(this._contentSize); } else { if (this._running && this._clippingStencil) this._clippingStencil.onExit(); this._clippingStencil = null; } break; default: break; } }, /** * Sets clipping type to ccui.Layout * @param {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} type */ setClippingType: function (type) { if (type == this._clippingType) return; var clippingEnabled = this.isClippingEnabled(); this.setClippingEnabled(false); this._clippingType = type; this.setClippingEnabled(clippingEnabled); }, /** * Gets clipping type of ccui.Layout * @returns {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} */ getClippingType: function () { return this._clippingType; }, _setStencilClippingSize: function (size) { if (this._clippingEnabled && this._clippingType == ccui.Layout.CLIPPING_STENCIL) { var rect = []; rect[0] = cc.p(0, 0); rect[1] = cc.p(size.width, 0); rect[2] = cc.p(size.width, size.height); rect[3] = cc.p(0, size.height); var green = cc.color.GREEN; this._clippingStencil.clear(); this._clippingStencil.drawPoly(rect, 4, green, 0, green); } }, _getClippingRect: function () { if (this._clippingRectDirty) { var worldPos = this.convertToWorldSpace(cc.p(0, 0)); var t = this.nodeToWorldTransform(); var scissorWidth = this._contentSize.width * t.a; var scissorHeight = this._contentSize.height * t.d; var parentClippingRect; var parent = this; while (parent) { parent = parent.getParent(); if (parent && parent instanceof ccui.Layout && parent.isClippingEnabled()) { this._clippingParent = parent; break; } } if (this._clippingParent) { parentClippingRect = this._clippingParent._getClippingRect(); var finalX = worldPos.x - (scissorWidth * this._anchorPoint.x); var finalY = worldPos.y - (scissorHeight * this._anchorPoint.y); var finalWidth = scissorWidth; var finalHeight = scissorHeight; var leftOffset = worldPos.x - parentClippingRect.x; if (leftOffset < 0) { finalX = parentClippingRect.x; finalWidth += leftOffset; } var rightOffset = (worldPos.x + scissorWidth) - (parentClippingRect.x + parentClippingRect.width); if (rightOffset > 0) finalWidth -= rightOffset; var topOffset = (worldPos.y + scissorHeight) - (parentClippingRect.y + parentClippingRect.height); if (topOffset > 0) finalHeight -= topOffset; var bottomOffset = worldPos.y - parentClippingRect.y; if (bottomOffset < 0) { finalY = parentClippingRect.x; finalHeight += bottomOffset; } if (finalWidth < 0) finalWidth = 0; if (finalHeight < 0) finalHeight = 0; this._clippingRect.x = finalX; this._clippingRect.y = finalY; this._clippingRect.width = finalWidth; this._clippingRect.height = finalHeight; } else { this._clippingRect.x = worldPos.x - (scissorWidth * this._anchorPoint.x); this._clippingRect.y = worldPos.y - (scissorHeight * this._anchorPoint.y); this._clippingRect.width = scissorWidth; this._clippingRect.height = scissorHeight; } this._clippingRectDirty = false; } return this._clippingRect; }, _onSizeChanged: function () { ccui.Widget.prototype._onSizeChanged.call(this); var locContentSize = this._contentSize; this._setStencilClippingSize(locContentSize); this._doLayoutDirty = true; this._clippingRectDirty = true; if (this._backGroundImage) { this._backGroundImage.setPosition(locContentSize.width * 0.5, locContentSize.height * 0.5); if (this._backGroundScale9Enabled && this._backGroundImage instanceof ccui.Scale9Sprite) this._backGroundImage.setPreferredSize(locContentSize); } if (this._colorRender) this._colorRender.setContentSize(locContentSize); if (this._gradientRender) this._gradientRender.setContentSize(locContentSize); }, /** * Sets background image use scale9 renderer. * @param {Boolean} able true that use scale9 renderer, false otherwise. */ setBackGroundImageScale9Enabled: function (able) { if (this._backGroundScale9Enabled == able) return; this.removeProtectedChild(this._backGroundImage); this._backGroundImage = null; this._backGroundScale9Enabled = able; this._addBackGroundImage(); this.setBackGroundImage(this._backGroundImageFileName, this._bgImageTexType); this.setBackGroundImageCapInsets(this._backGroundImageCapInsets); }, /** * Get whether background image is use scale9 renderer. * @returns {Boolean} */ isBackGroundImageScale9Enabled: function () { return this._backGroundScale9Enabled; }, /** * Sets a background image for layout * @param {String} fileName * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType */ setBackGroundImage: function (fileName, texType) { if (!fileName) return; texType = texType || ccui.Widget.LOCAL_TEXTURE; if (this._backGroundImage == null) this._addBackGroundImage(); this._backGroundImageFileName = fileName; this._bgImageTexType = texType; var locBackgroundImage = this._backGroundImage; if (this._backGroundScale9Enabled) { var bgiScale9 = locBackgroundImage; switch (this._bgImageTexType) { case ccui.Widget.LOCAL_TEXTURE: bgiScale9.initWithFile(fileName); break; case ccui.Widget.PLIST_TEXTURE: bgiScale9.initWithSpriteFrameName(fileName); break; default: break; } bgiScale9.setPreferredSize(this._contentSize); } else { var sprite = locBackgroundImage; switch (this._bgImageTexType){ case ccui.Widget.LOCAL_TEXTURE: //SetTexture cannot load resource sprite.initWithFile(fileName); break; case ccui.Widget.PLIST_TEXTURE: //SetTexture cannot load resource sprite.initWithSpriteFrameName(fileName); break; default: break; } } this._backGroundImageTextureSize = locBackgroundImage.getContentSize(); locBackgroundImage.setPosition(this._contentSize.width * 0.5, this._contentSize.height * 0.5); this._updateBackGroundImageColor(); /*//async load callback var self = this; if(!locBackgroundImage.texture || !locBackgroundImage.texture.isLoaded()){ locBackgroundImage.addLoadedEventListener(function(){ self._backGroundImageTextureSize = locBackgroundImage.getContentSize(); locBackgroundImage.setPosition(self._contentSize.width * 0.5, self._contentSize.height * 0.5); self._updateBackGroundImageColor(); self._imageRendererAdaptDirty = true; self._findLayout(); }); }*/ }, /** * Sets a background image CapInsets for layout, if the background image is a scale9 render. * @param {cc.Rect} capInsets capinsets of background image. */ setBackGroundImageCapInsets: function (capInsets) { if(!capInsets) return; var locInsets = this._backGroundImageCapInsets; locInsets.x = capInsets.x; locInsets.y = capInsets.y; locInsets.width = capInsets.width; locInsets.height = capInsets.height; if (this._backGroundScale9Enabled) this._backGroundImage.setCapInsets(capInsets); }, /** * Gets background image capinsets of ccui.Layout. * @returns {cc.Rect} */ getBackGroundImageCapInsets: function () { return cc.rect(this._backGroundImageCapInsets); }, _supplyTheLayoutParameterLackToChild: function (locChild) { if (!locChild) { return; } switch (this._layoutType) { case ccui.Layout.ABSOLUTE: break; case ccui.Layout.LINEAR_HORIZONTAL: case ccui.Layout.LINEAR_VERTICAL: var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.LINEAR); if (!layoutParameter) locChild.setLayoutParameter(ccui.LinearLayoutParameter.create()); break; case ccui.Layout.RELATIVE: var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.RELATIVE); if (!layoutParameter) locChild.setLayoutParameter(ccui.RelativeLayoutParameter.create()); break; default: break; } }, _addBackGroundImage: function () { if (this._backGroundScale9Enabled) { this._backGroundImage = ccui.Scale9Sprite.create(); this._backGroundImage.setPreferredSize(this._contentSize); } else this._backGroundImage = cc.Sprite.create(); this.addProtectedChild(this._backGroundImage, ccui.Layout.BACKGROUND_IMAGE_ZORDER, -1); this._backGroundImage.setPosition(this._contentSize.width / 2.0, this._contentSize.height / 2.0); }, /** * Remove the background image of ccui.Layout. */ removeBackGroundImage: function () { if (!this._backGroundImage) return; this.removeProtectedChild(this._backGroundImage); this._backGroundImage = null; this._backGroundImageFileName = ""; this._backGroundImageTextureSize.width = 0; this._backGroundImageTextureSize.height = 0; }, /** * Sets Color Type for ccui.Layout. * @param {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT} type */ setBackGroundColorType: function (type) { if (this._colorType == type) return; switch (this._colorType) { case ccui.Layout.BG_COLOR_NONE: if (this._colorRender) { this.removeProtectedChild(this._colorRender); this._colorRender = null; } if (this._gradientRender) { this.removeProtectedChild(this._gradientRender); this._gradientRender = null; } break; case ccui.Layout.BG_COLOR_SOLID: if (this._colorRender) { this.removeProtectedChild(this._colorRender); this._colorRender = null; } break; case ccui.Layout.BG_COLOR_GRADIENT: if (this._gradientRender) { this.removeProtectedChild(this._gradientRender); this._gradientRender = null; } break; default: break; } this._colorType = type; switch (this._colorType) { case ccui.Layout.BG_COLOR_NONE: break; case ccui.Layout.BG_COLOR_SOLID: this._colorRender = new cc.LayerColor(); this._colorRender.setContentSize(this._contentSize); this._colorRender.setOpacity(this._opacity); this._colorRender.setColor(this._color); this.addProtectedChild(this._colorRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1); break; case ccui.Layout.BG_COLOR_GRADIENT: this._gradientRender = new cc.LayerGradient(cc.color(255, 0, 0, 255), cc.color(0, 255, 0, 255)); this._gradientRender.setContentSize(this._contentSize); this._gradientRender.setOpacity(this._opacity); this._gradientRender.setStartColor(this._startColor); this._gradientRender.setEndColor(this._endColor); this._gradientRender.setVector(this._alongVector); this.addProtectedChild(this._gradientRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1); break; default: break; } }, /** * Get background color type of ccui.Layout. * @returns {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT} */ getBackGroundColorType: function () { return this._colorType; }, /** * Sets background color for layout, if color type is Layout.COLOR_SOLID * @param {cc.Color} color * @param {cc.Color} [endColor] */ setBackGroundColor: function (color, endColor) { if (!endColor) { this._color.r = color.r; this._color.g = color.g; this._color.b = color.b; if (this._colorRender) this._colorRender.setColor(color); } else { this._startColor.r = color.r; this._startColor.g = color.g; this._startColor.b = color.b; if (this._gradientRender) this._gradientRender.setStartColor(color); this._endColor.r = endColor.r; this._endColor.g = endColor.g; this._endColor.b = endColor.b; if (this._gradientRender) this._gradientRender.setEndColor(endColor); } }, /** * Gets background color of ccui.Layout, if color type is Layout.COLOR_SOLID. * @returns {cc.Color} */ getBackGroundColor: function () { var tmpColor = this._color; return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a); }, /** * Gets background start color of ccui.Layout * @returns {cc.Color} */ getBackGroundStartColor: function () { var tmpColor = this._startColor; return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a); }, /** * Gets background end color of ccui.Layout * @returns {cc.Color} */ getBackGroundEndColor: function () { var tmpColor = this._endColor; return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a); }, /** * Sets background opacity to ccui.Layout. * @param {number} opacity */ setBackGroundColorOpacity: function (opacity) { this._opacity = opacity; switch (this._colorType) { case ccui.Layout.BG_COLOR_NONE: break; case ccui.Layout.BG_COLOR_SOLID: this._colorRender.setOpacity(opacity); break; case ccui.Layout.BG_COLOR_GRADIENT: this._gradientRender.setOpacity(opacity); break; default: break; } }, /** * Get background opacity value of ccui.Layout. * @returns {Number} */ getBackGroundColorOpacity: function () { return this._opacity; }, /** * Sets background color vector for layout, if color type is Layout.COLOR_GRADIENT * @param {cc.Point} vector */ setBackGroundColorVector: function (vector) { this._alongVector.x = vector.x; this._alongVector.y = vector.y; if (this._gradientRender) { this._gradientRender.setVector(vector); } }, /** * Gets background color vector of ccui.Layout, if color type is Layout.COLOR_GRADIENT * @returns {cc.Point} */ getBackGroundColorVector: function () { return this._alongVector; }, /** * Sets backGround image color * @param {cc.Color} color */ setBackGroundImageColor: function (color) { this._backGroundImageColor.r = color.r; this._backGroundImageColor.g = color.g; this._backGroundImageColor.b = color.b; this._updateBackGroundImageColor(); }, /** * Sets backGround image Opacity * @param {Number} opacity */ setBackGroundImageOpacity: function (opacity) { this._backGroundImageColor.a = opacity; this.getBackGroundImageColor(); }, /** * Gets backGround image color * @returns {cc.Color} */ getBackGroundImageColor: function () { var color = this._backGroundImageColor; return cc.color(color.r, color.g, color.b, color.a); }, /** * Gets backGround image opacity * @returns {Number} */ getBackGroundImageOpacity: function () { return this._backGroundImageColor.a; }, _updateBackGroundImageColor: function () { if(this._backGroundImage) this._backGroundImage.setColor(this._backGroundImageColor); }, /** * Gets background image texture size. * @returns {cc.Size} */ getBackGroundImageTextureSize: function () { return this._backGroundImageTextureSize; }, /** * Sets LayoutType to ccui.Layout, LayoutManager will do layout by layout type.. * @param {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} type */ setLayoutType: function (type) { this._layoutType = type; var layoutChildrenArray = this._children; var locChild = null; for (var i = 0; i < layoutChildrenArray.length; i++) { locChild = layoutChildrenArray[i]; if(locChild instanceof ccui.Widget) this._supplyTheLayoutParameterLackToChild(locChild); } this._doLayoutDirty = true; }, /** * Gets LayoutType of ccui.Layout. * @returns {null} */ getLayoutType: function () { return this._layoutType; }, /** * request do layout, it will do layout at visit calls */ requestDoLayout: function () { this._doLayoutDirty = true; }, _doLayout: function () { if (!this._doLayoutDirty) return; this.sortAllChildren(); var executant = ccui.getLayoutManager(this._layoutType); if (executant) executant._doLayout(this); this._doLayoutDirty = false; }, _getLayoutContentSize: function(){ return this.getContentSize(); }, _getLayoutElements: function(){ return this.getChildren(); }, //clipping _onBeforeVisitStencil: function(){ //TODO NEW RENDERER }, _drawFullScreenQuadClearStencil:function(){ //TODO NEW RENDERER }, _onAfterDrawStencil: function(){ //TODO NEW RENDERER }, _onAfterVisitStencil: function(){ //TODO NEW RENDERER }, _onAfterVisitScissor: function(){ //TODO NEW RENDERER }, _onAfterVisitScissor: function(){ //TODO NEW RENDERER }, _updateBackGroundImageOpacity: function(){ if (this._backGroundImage) this._backGroundImage.setOpacity(this._backGroundImageOpacity); }, _updateBackGroundImageRGBA: function(){ if (this._backGroundImage) { this._backGroundImage.setColor(this._backGroundImageColor); this._backGroundImage.setOpacity(this._backGroundImageOpacity); } }, /** * Gets the content size of the layout, it will accumulate all its children's content size * @returns {cc.Size} * @private */ _getLayoutAccumulatedSize: function(){ var children = this.getChildren(); var layoutSize = cc.size(0, 0); var widgetCount = 0, locSize; for(var i = 0, len = children.length; i < len; i++) { var layout = children[i]; if (null != layout && layout instanceof ccui.Layout){ locSize = layout._getLayoutAccumulatedSize(); layoutSize.width += locSize.width; layoutSize.height += locSize.height; } else { if (layout instanceof ccui.Widget) { widgetCount++; var m = layout.getLayoutParameter().getMargin(); locSize = layout.getContentSize(); layoutSize.width += locSize.width + (m.right + m.left) * 0.5; layoutSize.height += locSize.height + (m.top + m.bottom) * 0.5; } } } //substract extra size var type = this.getLayoutType(); if (type == ccui.Layout.LINEAR_HORIZONTAL) layoutSize.height = layoutSize.height - layoutSize.height/widgetCount * (widgetCount-1); if (type == ccui.Layout.LINEAR_VERTICAL) layoutSize.width = layoutSize.width - layoutSize.width/widgetCount * (widgetCount-1); return layoutSize; }, /** * When the layout get focused, it the layout pass the focus to its child, it will use this method to determine which child