首页 > 代码库 > 【Cocos2d-x】源码分析之 2d/ui/UILayout

【Cocos2d-x】源码分析之 2d/ui/UILayout

#ifndef __LAYOUT_H__
#define __LAYOUT_H__

#include "ui/UIWidget.h"

NS_CC_BEGIN

namespace ui {

typedef enum
{
    LAYOUT_COLOR_NONE,//空
    LAYOUT_COLOR_SOLID,//单一固定颜色的
    LAYOUT_COLOR_GRADIENT//有梯度变化的
}LayoutBackGroundColorType;//容器背景颜色类型

typedef enum
{
    LAYOUT_ABSOLUTE,//绝对
    LAYOUT_LINEAR_VERTICAL,//线性 垂直
    LAYOUT_LINEAR_HORIZONTAL,//线性 水平
    LAYOUT_RELATIVE// 平面
}LayoutType;//容器类型

typedef enum {
    LAYOUT_CLIPPING_STENCIL,//模板
    LAYOUT_CLIPPING_SCISSOR//镂空
}LayoutClippingType;//容器裁切类型

/**
 *  @js NA
 *  @lua NA
 */
class LayoutExecutant;
    
class Layout : public Widget
{
    
    DECLARE_CLASS_GUI_INFO
    
public:
    
    Layout();
    virtual ~Layout();
    static Layout* create();
        
    //background
    /**设置背景图
     * @param fileName 图片路径
     * @param texType @see TextureResType.
     */
    void setBackGroundImage(const std::string& fileName,TextureResType texType = UI_TEX_TYPE_LOCAL);
    
    /**如果背景图是9宫格 设置拉伸尺度
     * @param capinsets of background image.
     */
    void setBackGroundImageCapInsets(const Rect& capInsets);
    
    const Rect& getBackGroundImageCapInsets();
    
    /**设置背景颜色类型*/
    void setBackGroundColorType(LayoutBackGroundColorType type);
    
    LayoutBackGroundColorType getBackGroundColorType();
    
    /**设置背景是否使用9宫格 默认不使用*/
    void setBackGroundImageScale9Enabled(bool enabled);
    
    bool isBackGroundImageScale9Enabled();
    
    /**设置背景颜色, 如果颜色类型是固定的*/
    void setBackGroundColor(const Color3B &color);
    
    const Color3B& getBackGroundColor();
    
    /**设置背景颜色, 如果颜色类型是 渐变的
     * @param start color
     * @param end color
     */
    void setBackGroundColor(const Color3B &startColor, const Color3B &endColor);
    
    const Color3B& getBackGroundStartColor();
    
    const Color3B& getBackGroundEndColor();
    
    /** 设置透明度.*/
    void setBackGroundColorOpacity(GLubyte opacity);
    
    GLubyte getBackGroundColorOpacity();
    
    /** 如果背景颜色是渐变的 设置背景颜色渐变的向量
     * @param vector
     */
    void setBackGroundColorVector(const Point &vector);
    
    const Point& getBackGroundColorVector();
    
    void setBackGroundImageColor(const Color3B& color);
    
    void setBackGroundImageOpacity(GLubyte opacity);
    
    const Color3B& getBackGroundImageColor();
    
    GLubyte getBackGroundImageOpacity();
    
    /**
     * 移除背景img
     */
    void removeBackGroundImage();
    
    /**得到背景img size*/
    const Size& getBackGroundImageTextureSize() const;
    
    /**
     * 设置布局可以裁切他的内荣和孩子 默认不裁切
     *如果你真的需要这个,请启用该功能。但它会降低渲染效率。
     * @param clipping enabled.
     */
    virtual void setClippingEnabled(bool enabled);
    /**设置裁切类型*/
    void setClippingType(LayoutClippingType type);
    
    LayoutClippingType getClippingType();
    
    /**是否可以裁切*/
    virtual bool isClippingEnabled();
    
    /**
     * Returns the "class name" of widget.
     */
    virtual std::string getDescription() const override;
    
    /**设置容器类型*/
    virtual void setLayoutType(LayoutType type);
    
    /**得到容器类型*/
    virtual LayoutType getLayoutType() const;

    virtual void addChild(Node * child) override;
    
    virtual void addChild(Node * child, int zOrder) override;
    
    virtual void addChild(Node* child, int zOrder, int tag) override;
    
    virtual void visit(Renderer *renderer, const kmMat4 &parentTransform, bool parentTransformUpdated) override;

    virtual void removeChild(Node* child, bool cleanup = true) override;
    
   
    virtual void removeAllChildren() override;
    /**
     * Removes all children from the container,并做了清理所有正在运行的动作 
     */
    virtual void removeAllChildrenWithCleanup(bool cleanup) override;

    virtual void sortAllChildren() override;
    
    void requestDoLayout();
    
    virtual void onEnter() override;
    virtual void onExit() override;
        
CC_CONSTRUCTOR_ACCESS:
    //override "init" method of widget.
    virtual bool init() override;
    
protected:
    //override "onSizeChanged" method of widget.
    virtual void onSizeChanged() override;
    
    //init background image renderer.
    void addBackGroundImage();
    
    void supplyTheLayoutParameterLackToChild(Widget* child);
    virtual Widget* createCloneInstance() override;
    virtual void copySpecialProperties(Widget* model) override;
    virtual void copyClonedWidgetChildren(Widget* model) override;
    /**模板类型的裁切渲染*/
    void stencilClippingVisit(Renderer *renderer, const kmMat4& parentTransform, bool parentTransformUpdated);
    /**镂空类型的裁切渲染*/
    void scissorClippingVisit(Renderer *renderer, const kmMat4& parentTransform, bool parentTransformUpdated);
    /**设置裁切模板size*/
    void setStencilClippingSize(const Size& size);
    const Rect& getClippingRect();
    virtual void doLayout();
    
    //clipping
    void onBeforeVisitStencil();
    void onAfterDrawStencil();
    void onAfterVisitStencil();
    
    void onBeforeVisitScissor();
    void onAfterVisitScissor();
    void updateBackGroundImageColor();
    void updateBackGroundImageOpacity();
    void updateBackGroundImageRGBA();
    LayoutExecutant* createCurrentLayoutExecutant();
protected:
    bool _clippingEnabled;//是否裁切
    
    //background
    bool _backGroundScale9Enabled;//是否开启9宫格
    Node* _backGroundImage;//背景图
    std::string _backGroundImageFileName;//背景name
    Rect _backGroundImageCapInsets;//拉伸区域
    LayoutBackGroundColorType _colorType;//背景颜色类型
    TextureResType _bgImageTexType;//贴图类型
    LayerColor* _colorRender;//正常渲染层 当前只用一个 由类型决定
    LayerGradient* _gradientRender;//渐变渲染层
    Color3B _cColor;//正常模式颜色值
    Color3B _gStartColor;//渐变模式颜色的开始值
    Color3B _gEndColor;//渐变模式颜色的结束值
    Point _alongVector;//渐变颜色方向
    GLubyte _cOpacity;//透明度
    Size _backGroundImageTextureSize;//背景贴图size
    LayoutType _layoutType;//容器类型
    LayoutClippingType _clippingType;//裁切类型
    DrawNode* _clippingStencil;//绘制几何裁切区域
    bool _scissorRectDirty;//多余的变量
    Rect _clippingRect;//裁切的区域
    Layout* _clippingParent;//裁切布局
    bool _doLayoutDirty;//是否开启布局
    bool _clippingRectDirty;//是否计算裁切区域 默认true 由于计算裁切区域逻辑较复杂
    //所以计算一次之后就不计算了 _clippingRectDirty = false;
    
    //clipping

    GLboolean _currentStencilEnabled;
    GLuint _currentStencilWriteMask;
    GLenum _currentStencilFunc;
    GLint _currentStencilRef;
    GLuint _currentStencilValueMask;
    GLenum _currentStencilFail;
    GLenum _currentStencilPassDepthFail;
    GLenum _currentStencilPassDepthPass;
    GLboolean _currentDepthWriteMask;
    
    GLboolean _currentAlphaTestEnabled;
    GLenum _currentAlphaTestFunc;
    GLclampf _currentAlphaTestRef;
    
    
    Color3B _backGroundImageColor;
    GLubyte _backGroundImageOpacity;
    
    LayoutExecutant* _curLayoutExecutant;//布局执行者
    
    GLint _mask_layer_le;
    GroupCommand _groupCommand;
    CustomCommand _beforeVisitCmdStencil;
    CustomCommand _afterDrawStencilCmd;
    CustomCommand _afterVisitCmdStencil;
    CustomCommand _beforeVisitCmdScissor;
    CustomCommand _afterVisitCmdScissor;
};
    
}
NS_CC_END
#endif /* defined(__Layout__) */
#include "ui/UILayout.h"
#include "ui/UIHelper.h"
#include "extensions/GUI/CCControlExtension/CCScale9Sprite.h"
#include "kazmath/GL/matrix.h"
#include "CCGLProgram.h"
#include "CCShaderCache.h"
#include "CCDirector.h"
#include "CCDrawingPrimitives.h"
#include "renderer/CCRenderer.h"
#include "renderer/CCGroupCommand.h"
#include "renderer/CCCustomCommand.h"

NS_CC_BEGIN

namespace ui {
//布局执行者
class LayoutExecutant : public Ref
{
public:
    LayoutExecutant(){};
    virtual ~LayoutExecutant(){};
    static LayoutExecutant* create();
    virtual void doLayout(const Size& layoutSize, Vector<Node*> container){};
};
//线性垂直 布局执行者
class LinearVerticalLayoutExecutant : public LayoutExecutant
{
public:
    LinearVerticalLayoutExecutant(){};
    virtual ~LinearVerticalLayoutExecutant(){};
    static LinearVerticalLayoutExecutant* create();
    virtual void doLayout(const Size& layoutSize, Vector<Node*> container);
};
//线性水平 布局执行者
class LinearHorizontalLayoutExecutant : public LayoutExecutant
{
public:
    LinearHorizontalLayoutExecutant(){};
    virtual ~LinearHorizontalLayoutExecutant(){};
    static LinearHorizontalLayoutExecutant* create();
    virtual void doLayout(const Size& layoutSize, Vector<Node*> container);
};
//相对 布局执行者
class RelativeLayoutExecutant : public LayoutExecutant
{
public:
    RelativeLayoutExecutant(){};
    virtual ~RelativeLayoutExecutant(){};
    static RelativeLayoutExecutant* create();
    virtual void doLayout(const Size& layoutSize, Vector<Node*> container);
};
    
LayoutExecutant* LayoutExecutant::create()
{
    LayoutExecutant* exe = new LayoutExecutant();
    if (exe)
    {
        exe->autorelease();
        return exe;
    }
    CC_SAFE_DELETE(exe);
    return nullptr;
}
    
LinearVerticalLayoutExecutant* LinearVerticalLayoutExecutant::create()
{
    LinearVerticalLayoutExecutant* exe = new LinearVerticalLayoutExecutant();
    if (exe)
    {
        exe->autorelease();
        return exe;
    }
    CC_SAFE_DELETE(exe);
    return nullptr;
}

LinearHorizontalLayoutExecutant* LinearHorizontalLayoutExecutant::create()
{
    LinearHorizontalLayoutExecutant* exe = new LinearHorizontalLayoutExecutant();
    if (exe)
    {
        exe->autorelease();
        return exe;
    }
    CC_SAFE_DELETE(exe);
    return nullptr;
}

RelativeLayoutExecutant* RelativeLayoutExecutant::create()
{
    RelativeLayoutExecutant* exe = new RelativeLayoutExecutant();
    if (exe)
    {
        exe->autorelease();
        return exe;
    }
    CC_SAFE_DELETE(exe);
    return nullptr;
}
//线性垂直 布局执行者
void LinearVerticalLayoutExecutant::doLayout(const cocos2d::Size &layoutSize, Vector<cocos2d::Node *> container)
{
    float topBoundary = layoutSize.height;//布局高度
    
    for (auto& subWidget : container)//遍历所有容器
    {
        Widget* child = dynamic_cast<Widget*>(subWidget);
        if (child)//是widget类型
        {
            LinearLayoutParameter* layoutParameter = dynamic_cast<LinearLayoutParameter*>(child->getLayoutParameter(LAYOUT_PARAMETER_LINEAR));
            
            if (layoutParameter)//是线性布局参数
            {
                LinearGravity childGravity = layoutParameter->getGravity();//重力类型
                Point ap = child->getAnchorPoint();//锚点
                Size cs = child->getSize();//大小
                float finalPosX = ap.x * cs.width;//最终X位置
                float finalPosY = topBoundary - ((1.0f-ap.y) * cs.height);//最终Y位置
                switch (childGravity)
                {
                    case LINEAR_GRAVITY_NONE:
                    case LINEAR_GRAVITY_LEFT:
                        break;
                    case LINEAR_GRAVITY_RIGHT:
                        finalPosX = layoutSize.width - ((1.0f - ap.x) * cs.width);//最终X位置
                        break;
                    case LINEAR_GRAVITY_CENTER_HORIZONTAL:
                        finalPosX = layoutSize.width / 2.0f - cs.width * (0.5f-ap.x);//最终Y位置
                        break;
                    default:
                        break;
                }
                Margin mg = layoutParameter->getMargin();//得到间距
                finalPosX += mg.left;//加上左间距 右移
                finalPosY -= mg.top;//减去上间距 下移
                child->setPosition(Point(finalPosX, finalPosY));
                topBoundary = child->getBottomInParent() - mg.bottom;
            }
        }
    }
}
//线性水平 布局执行者 类似上
void LinearHorizontalLayoutExecutant::doLayout(const cocos2d::Size &layoutSize, Vector<cocos2d::Node *> container)
{
    float leftBoundary = 0.0f;
    for (auto& subWidget : container)
    {
        Widget* child = dynamic_cast<Widget*>(subWidget);
        if (child)
        {
            LinearLayoutParameter* layoutParameter = dynamic_cast<LinearLayoutParameter*>(child->getLayoutParameter(LAYOUT_PARAMETER_LINEAR));
            if (layoutParameter)
            {
                LinearGravity childGravity = layoutParameter->getGravity();
                Point ap = child->getAnchorPoint();
                Size cs = child->getSize();
                float finalPosX = leftBoundary + (ap.x * cs.width);
                float finalPosY = layoutSize.height - (1.0f - ap.y) * cs.height;
                switch (childGravity)
                {
                    case LINEAR_GRAVITY_NONE:
                    case LINEAR_GRAVITY_TOP:
                        break;
                    case LINEAR_GRAVITY_BOTTOM:
                        finalPosY = ap.y * cs.height;
                        break;
                    case LINEAR_GRAVITY_CENTER_VERTICAL:
                        finalPosY = layoutSize.height / 2.0f - cs.height * (0.5f - ap.y);
                        break;
                    default:
                        break;
                }
                Margin mg = layoutParameter->getMargin();
                finalPosX += mg.left;
                finalPosY -= mg.top;
                child->setPosition(Point(finalPosX, finalPosY));
                leftBoundary = child->getRightInParent() + mg.right;
            }
        }
    }
}
//相对 布局执行者
void RelativeLayoutExecutant::doLayout(const cocos2d::Size &layoutSize, Vector<cocos2d::Node *> container)
{
    ssize_t unlayoutChildCount = 0;
    Vector<Widget*> widgetChildren;
    for (auto& subWidget : container)
    {
        Widget* child = dynamic_cast<Widget*>(subWidget);
        if (child)
        {
            RelativeLayoutParameter* layoutParameter = dynamic_cast<RelativeLayoutParameter*>(child->getLayoutParameter(LAYOUT_PARAMETER_RELATIVE));
            layoutParameter->_put = false;
            unlayoutChildCount++;
            widgetChildren.pushBack(child);
        }
    }
    while (unlayoutChildCount > 0)
    {
        for (auto& subWidget : widgetChildren)
        {
            Widget* child = static_cast<Widget*>(subWidget);
            RelativeLayoutParameter* layoutParameter = dynamic_cast<RelativeLayoutParameter*>(child->getLayoutParameter(LAYOUT_PARAMETER_RELATIVE));
            
            if (layoutParameter)
            {
                if (layoutParameter->_put)
                {
                    continue;
                }
                Point ap = child->getAnchorPoint();
                Size cs = child->getSize();
                RelativeAlign align = layoutParameter->getAlign();
                const char* relativeName = layoutParameter->getRelativeToWidgetName();
                Widget* relativeWidget = nullptr;
                RelativeLayoutParameter* relativeWidgetLP = nullptr;
                float finalPosX = 0.0f;
                float finalPosY = 0.0f;
                if (relativeName && strcmp(relativeName, ""))
                {
                    for (auto& sWidget : widgetChildren)
                    {
                        if (sWidget)
                        {
                            RelativeLayoutParameter* rlayoutParameter = dynamic_cast<RelativeLayoutParameter*>(sWidget->getLayoutParameter(LAYOUT_PARAMETER_RELATIVE));
                            if (rlayoutParameter && strcmp(rlayoutParameter->getRelativeName(), relativeName) == 0)
                            {
                                relativeWidget = sWidget;
                                relativeWidgetLP = rlayoutParameter;
                                break;
                            }
                        }
                    }
                }
                switch (align)
                {
                    case RELATIVE_ALIGN_NONE:
                    case RELATIVE_ALIGN_PARENT_TOP_LEFT:
                        finalPosX = ap.x * cs.width;
                        finalPosY = layoutSize.height - ((1.0f - ap.y) * cs.height);
                        break;
                    case RELATIVE_ALIGN_PARENT_TOP_CENTER_HORIZONTAL:
                        finalPosX = layoutSize.width * 0.5f - cs.width * (0.5f - ap.x);
                        finalPosY = layoutSize.height - ((1.0f - ap.y) * cs.height);
                        break;
                    case RELATIVE_ALIGN_PARENT_TOP_RIGHT:
                        finalPosX = layoutSize.width - ((1.0f - ap.x) * cs.width);
                        finalPosY = layoutSize.height - ((1.0f - ap.y) * cs.height);
                        break;
                    case RELATIVE_ALIGN_PARENT_LEFT_CENTER_VERTICAL:
                        finalPosX = ap.x * cs.width;
                        finalPosY = layoutSize.height * 0.5f - cs.height * (0.5f - ap.y);
                        break;
                    case RELATIVE_CENTER_IN_PARENT:
                        finalPosX = layoutSize.width * 0.5f - cs.width * (0.5f - ap.x);
                        finalPosY = layoutSize.height * 0.5f - cs.height * (0.5f - ap.y);
                        break;
                    case RELATIVE_ALIGN_PARENT_RIGHT_CENTER_VERTICAL:
                        finalPosX = layoutSize.width - ((1.0f - ap.x) * cs.width);
                        finalPosY = layoutSize.height * 0.5f - cs.height * (0.5f - ap.y);
                        break;
                    case RELATIVE_ALIGN_PARENT_LEFT_BOTTOM:
                        finalPosX = ap.x * cs.width;
                        finalPosY = ap.y * cs.height;
                        break;
                    case RELATIVE_ALIGN_PARENT_BOTTOM_CENTER_HORIZONTAL:
                        finalPosX = layoutSize.width * 0.5f - cs.width * (0.5f - ap.x);
                        finalPosY = ap.y * cs.height;
                        break;
                    case RELATIVE_ALIGN_PARENT_RIGHT_BOTTOM:
                        finalPosX = layoutSize.width - ((1.0f - ap.x) * cs.width);
                        finalPosY = ap.y * cs.height;
                        break;
                        
                    case RELATIVE_LOCATION_ABOVE_LEFTALIGN:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            float locationBottom = relativeWidget->getTopInParent();
                            float locationLeft = relativeWidget->getLeftInParent();
                            finalPosY = locationBottom + ap.y * cs.height;
                            finalPosX = locationLeft + ap.x * cs.width;
                        }
                        break;
                    case RELATIVE_LOCATION_ABOVE_CENTER:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            Size rbs = relativeWidget->getSize();
                            float locationBottom = relativeWidget->getTopInParent();
                            
                            finalPosY = locationBottom + ap.y * cs.height;
                            finalPosX = relativeWidget->getLeftInParent() + rbs.width * 0.5f + ap.x * cs.width - cs.width * 0.5f;
                        }
                        break;
                    case RELATIVE_LOCATION_ABOVE_RIGHTALIGN:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            float locationBottom = relativeWidget->getTopInParent();
                            float locationRight = relativeWidget->getRightInParent();
                            finalPosY = locationBottom + ap.y * cs.height;
                            finalPosX = locationRight - (1.0f - ap.x) * cs.width;
                        }
                        break;
                    case RELATIVE_LOCATION_LEFT_OF_TOPALIGN:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            float locationTop = relativeWidget->getTopInParent();
                            float locationRight = relativeWidget->getLeftInParent();
                            finalPosY = locationTop - (1.0f - ap.y) * cs.height;
                            finalPosX = locationRight - (1.0f - ap.x) * cs.width;
                        }
                        break;
                    case RELATIVE_LOCATION_LEFT_OF_CENTER:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            Size rbs = relativeWidget->getSize();
                            float locationRight = relativeWidget->getLeftInParent();
                            finalPosX = locationRight - (1.0f - ap.x) * cs.width;
                            
                            finalPosY = relativeWidget->getBottomInParent() + rbs.height * 0.5f + ap.y * cs.height - cs.height * 0.5f;
                        }
                        break;
                    case RELATIVE_LOCATION_LEFT_OF_BOTTOMALIGN:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            float locationBottom = relativeWidget->getBottomInParent();
                            float locationRight = relativeWidget->getLeftInParent();
                            finalPosY = locationBottom + ap.y * cs.height;
                            finalPosX = locationRight - (1.0f - ap.x) * cs.width;
                        }
                        break;
                    case RELATIVE_LOCATION_RIGHT_OF_TOPALIGN:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            float locationTop = relativeWidget->getTopInParent();
                            float locationLeft = relativeWidget->getRightInParent();
                            finalPosY = locationTop - (1.0f - ap.y) * cs.height;
                            finalPosX = locationLeft + ap.x * cs.width;
                        }
                        break;
                    case RELATIVE_LOCATION_RIGHT_OF_CENTER:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            Size rbs = relativeWidget->getSize();
                            float locationLeft = relativeWidget->getRightInParent();
                            finalPosX = locationLeft + ap.x * cs.width;
                            
                            finalPosY = relativeWidget->getBottomInParent() + rbs.height * 0.5f + ap.y * cs.height - cs.height * 0.5f;
                        }
                        break;
                    case RELATIVE_LOCATION_RIGHT_OF_BOTTOMALIGN:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            float locationBottom = relativeWidget->getBottomInParent();
                            float locationLeft = relativeWidget->getRightInParent();
                            finalPosY = locationBottom + ap.y * cs.height;
                            finalPosX = locationLeft + ap.x * cs.width;
                        }
                        break;
                    case RELATIVE_LOCATION_BELOW_LEFTALIGN:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            float locationTop = relativeWidget->getBottomInParent();
                            float locationLeft = relativeWidget->getLeftInParent();
                            finalPosY = locationTop - (1.0f - ap.y) * cs.height;
                            finalPosX = locationLeft + ap.x * cs.width;
                        }
                        break;
                    case RELATIVE_LOCATION_BELOW_CENTER:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            Size rbs = relativeWidget->getSize();
                            float locationTop = relativeWidget->getBottomInParent();
                            
                            finalPosY = locationTop - (1.0f - ap.y) * cs.height;
                            finalPosX = relativeWidget->getLeftInParent() + rbs.width * 0.5f + ap.x * cs.width - cs.width * 0.5f;
                        }
                        break;
                    case RELATIVE_LOCATION_BELOW_RIGHTALIGN:
                        if (relativeWidget)
                        {
                            if (relativeWidgetLP && !relativeWidgetLP->_put)
                            {
                                continue;
                            }
                            float locationTop = relativeWidget->getBottomInParent();
                            float locationRight = relativeWidget->getRightInParent();
                            finalPosY = locationTop - (1.0f - ap.y) * cs.height;
                            finalPosX = locationRight - (1.0f - ap.x) * cs.width;
                        }
                        break;
                    default:
                        break;
                }
                Margin relativeWidgetMargin;
                Margin mg = layoutParameter->getMargin();
                if (relativeWidgetLP)
                {
                    relativeWidgetMargin = relativeWidgetLP->getMargin();
                }
                //handle margin
                switch (align)
                {
                    case RELATIVE_ALIGN_NONE:
                    case RELATIVE_ALIGN_PARENT_TOP_LEFT:
                        finalPosX += mg.left;
                        finalPosY -= mg.top;
                        break;
                    case RELATIVE_ALIGN_PARENT_TOP_CENTER_HORIZONTAL:
                        finalPosY -= mg.top;
                        break;
                    case RELATIVE_ALIGN_PARENT_TOP_RIGHT:
                        finalPosX -= mg.right;
                        finalPosY -= mg.top;
                        break;
                    case RELATIVE_ALIGN_PARENT_LEFT_CENTER_VERTICAL:
                        finalPosX += mg.left;
                        break;
                    case RELATIVE_CENTER_IN_PARENT:
                        break;
                    case RELATIVE_ALIGN_PARENT_RIGHT_CENTER_VERTICAL:
                        finalPosX -= mg.right;
                        break;
                    case RELATIVE_ALIGN_PARENT_LEFT_BOTTOM:
                        finalPosX += mg.left;
                        finalPosY += mg.bottom;
                        break;
                    case RELATIVE_ALIGN_PARENT_BOTTOM_CENTER_HORIZONTAL:
                        finalPosY += mg.bottom;
                        break;
                    case RELATIVE_ALIGN_PARENT_RIGHT_BOTTOM:
                        finalPosX -= mg.right;
                        finalPosY += mg.bottom;
                        break;
                        
                    case RELATIVE_LOCATION_ABOVE_LEFTALIGN:
                        finalPosY += mg.bottom;
                        finalPosX += mg.left;
                        break;
                    case RELATIVE_LOCATION_ABOVE_RIGHTALIGN:
                        finalPosY += mg.bottom;
                        finalPosX -= mg.right;
                        break;
                    case RELATIVE_LOCATION_ABOVE_CENTER:
                        finalPosY += mg.bottom;
                        break;
                        
                    case RELATIVE_LOCATION_LEFT_OF_TOPALIGN:
                        finalPosX -= mg.right;
                        finalPosY -= mg.top;
                        break;
                    case RELATIVE_LOCATION_LEFT_OF_BOTTOMALIGN:
                        finalPosX -= mg.right;
                        finalPosY += mg.bottom;
                        break;
                    case RELATIVE_LOCATION_LEFT_OF_CENTER:
                        finalPosX -= mg.right;
                        break;
                        
                    case RELATIVE_LOCATION_RIGHT_OF_TOPALIGN:
                        finalPosX += mg.left;
                        finalPosY -= mg.top;
                        break;
                    case RELATIVE_LOCATION_RIGHT_OF_BOTTOMALIGN:
                        finalPosX += mg.left;
                        finalPosY += mg.bottom;
                        break;
                    case RELATIVE_LOCATION_RIGHT_OF_CENTER:
                        finalPosX += mg.left;
                        break;
                        
                    case RELATIVE_LOCATION_BELOW_LEFTALIGN:
                        finalPosY -= mg.top;
                        finalPosX += mg.left;
                        break;
                    case RELATIVE_LOCATION_BELOW_RIGHTALIGN:
                        finalPosY -= mg.top;
                        finalPosX -= mg.right;
                        break;
                    case RELATIVE_LOCATION_BELOW_CENTER:
                        finalPosY -= mg.top;
                        break;
                    default:
                        break;
                }
                child->setPosition(Point(finalPosX, finalPosY));
                layoutParameter->_put = true;
                unlayoutChildCount--;
            }
        }
    }
    widgetChildren.clear();
}
//渲染层Zorder
static const int BACKGROUNDIMAGE_Z = (-1);
static const int BCAKGROUNDCOLORRENDERER_Z = (-2);

static GLint g_sStencilBits = -1;
static GLint s_layer = -1;
    
IMPLEMENT_CLASS_GUI_INFO(Layout)

Layout::Layout():
_clippingEnabled(false),
_backGroundScale9Enabled(false),
_backGroundImage(nullptr),
_backGroundImageFileName(""),
_backGroundImageCapInsets(Rect::ZERO),
_colorType(LAYOUT_COLOR_NONE),
_bgImageTexType(UI_TEX_TYPE_LOCAL),
_colorRender(nullptr),
_gradientRender(nullptr),
_cColor(Color3B::WHITE),
_gStartColor(Color3B::WHITE),
_gEndColor(Color3B::WHITE),
_alongVector(Point(0.0f, -1.0f)),
_cOpacity(255),
_backGroundImageTextureSize(Size::ZERO),
_layoutType(LAYOUT_ABSOLUTE),
_clippingType(LAYOUT_CLIPPING_STENCIL),
_clippingStencil(nullptr),
_scissorRectDirty(false),
_clippingRect(Rect::ZERO),
_clippingParent(nullptr),
_doLayoutDirty(true),
_clippingRectDirty(true),
_currentStencilEnabled(GL_FALSE),
_currentStencilWriteMask(~0),
_currentStencilFunc(GL_ALWAYS),
_currentStencilRef(0),
_currentStencilValueMask(~0),
_currentStencilFail(GL_KEEP),
_currentStencilPassDepthFail(GL_KEEP),
_currentStencilPassDepthPass(GL_KEEP),
_currentDepthWriteMask(GL_TRUE),
_currentAlphaTestEnabled(GL_FALSE),
_currentAlphaTestFunc(GL_ALWAYS),
_currentAlphaTestRef(1),
_backGroundImageColor(Color3B::WHITE),
_backGroundImageOpacity(255),
_curLayoutExecutant(nullptr)
{
    _widgetType = WidgetTypeContainer;//默认 为容器类型
}

Layout::~Layout()
{
    CC_SAFE_RELEASE(_clippingStencil);
    CC_SAFE_RELEASE(_curLayoutExecutant);
}
    
void Layout::onEnter()
{
    Widget::onEnter();
    if (_clippingStencil)
    {
        _clippingStencil->onEnter();
    }
    _doLayoutDirty = true;//可以布局
    _clippingRectDirty = true;//可以计算裁切矩形
}
    
void Layout::onExit()
{
    Widget::onExit();
    if (_clippingStencil)
    {
        _clippingStencil->onExit();
    }
}

Layout* Layout::create()
{
    Layout* layout = new Layout();
    if (layout && layout->init())
    {
        layout->autorelease();
        return layout;
    }
    CC_SAFE_DELETE(layout);
    return nullptr;
}

bool Layout::init()
{
    if (ProtectedNode::init())
    {
        initRenderer();
        setBright(true);
        ignoreContentAdaptWithSize(false);
        setSize(Size::ZERO);
        setAnchorPoint(Point::ZERO);//锚点左下
        return true;
    }
    return false;
}
    
void Layout::addChild(Node *child)
{
    Widget::addChild(child);
}

void Layout::addChild(Node * child, int zOrder)
{
    Widget::addChild(child, zOrder);
}

void Layout::addChild(Node *child, int zOrder, int tag)
{
    supplyTheLayoutParameterLackToChild(static_cast<Widget*>(child));//设置布局参数
    Widget::addChild(child, zOrder, tag);//添加child
    _doLayoutDirty = true;
}
    
void Layout::removeChild(Node *child, bool cleanup)
{
    Widget::removeChild(child, cleanup);
    _doLayoutDirty = true;
}
    
void Layout::removeAllChildren()
{
    Widget::removeAllChildren();
}
    
void Layout::removeAllChildrenWithCleanup(bool cleanup)
{
    Widget::removeAllChildrenWithCleanup(cleanup);
    _doLayoutDirty = true;
}

bool Layout::isClippingEnabled()
{
    return _clippingEnabled;
}

void Layout::visit(Renderer *renderer, const kmMat4 &parentTransform, bool parentTransformUpdated)
{
    if (!_enabled)//不可用就直接return
    {
        return;
    }
    adaptRenderers();//对应各自的逻辑处理
    if (_clippingEnabled)//可以裁切
    {
        switch (_clippingType)//裁切类型
        {
            case LAYOUT_CLIPPING_STENCIL://模板
                stencilClippingVisit(renderer, parentTransform, parentTransformUpdated);//专属自己的visit
                break;
            case LAYOUT_CLIPPING_SCISSOR://镂空
                scissorClippingVisit(renderer, parentTransform, parentTransformUpdated);//专属自己的visit
                break;
            default:
                break;
        }
    }
    else//不可以裁切 基本的visit
    {
        ProtectedNode::visit(renderer, parentTransform, parentTransformUpdated);
    }
}
    
void Layout::sortAllChildren()
{
    Widget::sortAllChildren();//排序子节点
    doLayout();//布局
}
//模板类型裁切
void Layout::stencilClippingVisit(Renderer *renderer, const kmMat4 &parentTransform, bool parentTransformUpdated)
{
    if(!_visible)
        return;
    
    bool dirty = parentTransformUpdated || _transformUpdated;
    if(dirty)
        _modelViewTransform = transform(parentTransform);
    _transformUpdated = false;

    // IMPORTANT:
    // To ease the migration to v3.0, we still support the kmGL stack,
    // but it is deprecated and your code should not rely on it
    kmGLPushMatrix();
    kmGLLoadMatrix(&_modelViewTransform);

    //Add group command

    _groupCommand.init(_globalZOrder);
    renderer->addCommand(&_groupCommand);
    
    renderer->pushGroup(_groupCommand.getRenderQueueID());
    
    _beforeVisitCmdStencil.init(_globalZOrder);
    _beforeVisitCmdStencil.func = CC_CALLBACK_0(Layout::onBeforeVisitStencil, this);
    renderer->addCommand(&_beforeVisitCmdStencil);
    
    _clippingStencil->visit(renderer, _modelViewTransform, dirty);
    
    _afterDrawStencilCmd.init(_globalZOrder);
    _afterDrawStencilCmd.func = CC_CALLBACK_0(Layout::onAfterDrawStencil, this);
    renderer->addCommand(&_afterDrawStencilCmd);
    
    int i = 0;      // used by _children
    int j = 0;      // used by _protectedChildren
    
    sortAllChildren();
    sortAllProtectedChildren();
    
    //
    // draw children and protectedChildren zOrder < 0
    //
    for( ; i < _children.size(); i++ )
    {
        auto node = _children.at(i);
        
        if ( node && node->getLocalZOrder() < 0 )
            node->visit(renderer, _modelViewTransform, dirty);
        else
            break;
    }
    
    for( ; j < _protectedChildren.size(); j++ )
    {
        auto node = _protectedChildren.at(j);
        
        if ( node && node->getLocalZOrder() < 0 )
            node->visit(renderer, _modelViewTransform, dirty);
        else
            break;
    }
    
    //
    // draw self
    //
    this->draw(renderer, _modelViewTransform, dirty);
    
    //
    // draw children and protectedChildren zOrder >= 0
    //
    for(auto it=_protectedChildren.cbegin()+j; it != _protectedChildren.cend(); ++it)
        (*it)->visit(renderer, _modelViewTransform, dirty);
    
    for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
        (*it)->visit(renderer, _modelViewTransform, dirty);

    
    _afterVisitCmdStencil.init(_globalZOrder);
    _afterVisitCmdStencil.func = CC_CALLBACK_0(Layout::onAfterVisitStencil, this);
    renderer->addCommand(&_afterVisitCmdStencil);
    
    renderer->popGroup();
    
    kmGLPopMatrix();
}
    
void Layout::onBeforeVisitStencil()
{
    s_layer++;
    GLint mask_layer = 0x1 << s_layer;
    GLint mask_layer_l = mask_layer - 1;
    _mask_layer_le = mask_layer | mask_layer_l;
    _currentStencilEnabled = glIsEnabled(GL_STENCIL_TEST);
    glGetIntegerv(GL_STENCIL_WRITEMASK, (GLint *)&_currentStencilWriteMask);
    glGetIntegerv(GL_STENCIL_FUNC, (GLint *)&_currentStencilFunc);
    glGetIntegerv(GL_STENCIL_REF, &_currentStencilRef);
    glGetIntegerv(GL_STENCIL_VALUE_MASK, (GLint *)&_currentStencilValueMask);
    glGetIntegerv(GL_STENCIL_FAIL, (GLint *)&_currentStencilFail);
    glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, (GLint *)&_currentStencilPassDepthFail);
    glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, (GLint *)&_currentStencilPassDepthPass);
    
    glEnable(GL_STENCIL_TEST);
    CHECK_GL_ERROR_DEBUG();
    glStencilMask(mask_layer);
    glGetBooleanv(GL_DEPTH_WRITEMASK, &_currentDepthWriteMask);
    glDepthMask(GL_FALSE);
    glStencilFunc(GL_NEVER, mask_layer, mask_layer);
    glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
    kmGLMatrixMode(KM_GL_MODELVIEW);
    kmGLPushMatrix();
    kmGLLoadIdentity();
    
    kmGLMatrixMode(KM_GL_PROJECTION);
    kmGLPushMatrix();
    kmGLLoadIdentity();
    
    DrawPrimitives::drawSolidRect(Point(-1,-1), Point(1,1), Color4F(1, 1, 1, 1));
    
    kmGLMatrixMode(KM_GL_PROJECTION);
    kmGLPopMatrix();
    kmGLMatrixMode(KM_GL_MODELVIEW);
    kmGLPopMatrix();
    glStencilFunc(GL_NEVER, mask_layer, mask_layer);
    glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
}

void Layout::onAfterDrawStencil()
{
    glDepthMask(_currentDepthWriteMask);
    glStencilFunc(GL_EQUAL, _mask_layer_le, _mask_layer_le);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}


void Layout::onAfterVisitStencil()
{
    glStencilFunc(_currentStencilFunc, _currentStencilRef, _currentStencilValueMask);
    glStencilOp(_currentStencilFail, _currentStencilPassDepthFail, _currentStencilPassDepthPass);
    glStencilMask(_currentStencilWriteMask);
    if (!_currentStencilEnabled)
    {
        glDisable(GL_STENCIL_TEST);
    }
    s_layer--;
}
    
void Layout::onBeforeVisitScissor()
{
    Rect clippingRect = getClippingRect();
    glEnable(GL_SCISSOR_TEST);//裁切开始
    auto glview = Director::getInstance()->getOpenGLView();//得到OpenGL 窗口
    glview->setScissorInPoints(clippingRect.origin.x, clippingRect.origin.y, clippingRect.size.width, clippingRect.size.height);//根据裁切范围进行裁切
}

void Layout::onAfterVisitScissor()
{
    glDisable(GL_SCISSOR_TEST);//裁切终止
}
    
void Layout::scissorClippingVisit(Renderer *renderer, const kmMat4& parentTransform, bool parentTransformUpdated)
{//模板裁切渲染
    _beforeVisitCmdScissor.init(_globalZOrder);
    _beforeVisitCmdScissor.func = CC_CALLBACK_0(Layout::onBeforeVisitScissor, this);
    renderer->addCommand(&_beforeVisitCmdScissor);

    ProtectedNode::visit(renderer, parentTransform, parentTransformUpdated);
    
    _afterVisitCmdScissor.init(_globalZOrder);
    _afterVisitCmdScissor.func = CC_CALLBACK_0(Layout::onAfterVisitScissor, this);
    renderer->addCommand(&_afterVisitCmdScissor);
}

void Layout::setClippingEnabled(bool able)
{
    if (able == _clippingEnabled)//默认不能裁切 若和当前值相同 说明设置过 不必再详加判断 防止外部调用多次出现 不必要的重复逻辑
    {
        return;
    }
    _clippingEnabled = able;//是否可以裁切
    switch (_clippingType)
    {
        case LAYOUT_CLIPPING_STENCIL://裁切类型为 模板
            if (able)//若能裁切
            {
                static bool once = true;
                if (once)
                {
                    glGetIntegerv(GL_STENCIL_BITS, &g_sStencilBits);//裁切
                    if (g_sStencilBits <= 0)
                    {
                        CCLOG("Stencil buffer is not enabled.");
                    }
                    once = false;
                }
                _clippingStencil = DrawNode::create();//创建 绘制对象
                if (_running)//若该节点在运行
                {
                    _clippingStencil->onEnter();//初始化绘制对象
                }
                _clippingStencil->retain();//绘制对象引用次数++
                setStencilClippingSize(_size);//设置裁切区域
            }
            else//若不能裁切
            {
                if (_running)//若该节点在运行
                {
                    _clippingStencil->onExit();//绘制对象 退出
                }
                _clippingStencil->release();//计数--
                _clippingStencil = nullptr;//置空
            }
            break;
        default:
            break;
    }
}
    
void Layout::setClippingType(LayoutClippingType type)
{
    if (type == _clippingType)
    {
        return;
    }
    bool clippingEnabled = isClippingEnabled();
    setClippingEnabled(false);
    _clippingType = type;//设置裁切类型
    setClippingEnabled(clippingEnabled);
}
    
LayoutClippingType Layout::getClippingType()
{
    return _clippingType;
}
    
void Layout::setStencilClippingSize(const Size &size)
{
    if (_clippingEnabled && _clippingType == LAYOUT_CLIPPING_STENCIL)
    {//若可以裁切 裁切类型为 模板
        Point rect[4];//设置裁切范围
        rect[0] = Point::ZERO;
        rect[1] = Point(_size.width, 0);
        rect[2] = Point(_size.width, _size.height);
        rect[3] = Point(0, _size.height);
        Color4F green(0, 1, 0, 1);
        _clippingStencil->clear();
        _clippingStencil->drawPolygon(rect, 4, green, 0, green);//花多边形
    }
}
//得到裁切的矩形
const Rect& Layout::getClippingRect()
{
    if (_clippingRectDirty)
    {
        Point worldPos = convertToWorldSpace(Point::ZERO);//世界坐标
        AffineTransform t = getNodeToWorldAffineTransform();//仿射旋转
        float scissorWidth = _size.width*t.a;//裁切宽度
        float scissorHeight = _size.height*t.d;//裁切高度
        Rect parentClippingRect;//父节点裁切矩形
        Layout* parent = this;//从自己开始

        while (parent)
        {
            parent = dynamic_cast<Layout*>(parent->getParent());
            if(parent)//存在layout的父节点
            {
                if (parent->isClippingEnabled())//若该节点可以裁切
                {
                    _clippingParent = parent;//保存裁切节点
                    break;
                }
            }
        }
        
        if (_clippingParent)//如果找到裁切父节点
        {
            parentClippingRect = _clippingParent->getClippingRect();//得到裁切矩形范围
            float finalX = worldPos.x - (scissorWidth * _anchorPoint.x);
            float finalY = worldPos.y - (scissorHeight * _anchorPoint.y);
            float finalWidth = scissorWidth;
            float finalHeight = scissorHeight;//计算得出裁切范围
            
            float leftOffset = worldPos.x - parentClippingRect.origin.x;
            if (leftOffset < 0.0f)
            {
                finalX = parentClippingRect.origin.x;
                finalWidth += leftOffset;
            }
            float rightOffset = (worldPos.x + scissorWidth) - (parentClippingRect.origin.x + parentClippingRect.size.width);
            if (rightOffset > 0.0f)
            {
                finalWidth -= rightOffset;
            }
            float topOffset = (worldPos.y + scissorHeight) - (parentClippingRect.origin.y + parentClippingRect.size.height);
            if (topOffset > 0.0f)
            {
                finalHeight -= topOffset;
            }
            float bottomOffset = worldPos.y - parentClippingRect.origin.y;
            if (bottomOffset < 0.0f)
            {
                finalY = parentClippingRect.origin.x;
                finalHeight += bottomOffset;
            }
            if (finalWidth < 0.0f)
            {
                finalWidth = 0.0f;
            }
            if (finalHeight < 0.0f)
            {
                finalHeight = 0.0f;
            }
            _clippingRect.origin.x = finalX;
            _clippingRect.origin.y = finalY;
            _clippingRect.size.width = finalWidth;
            _clippingRect.size.height = finalHeight;
        }
        else
        {
            _clippingRect.origin.x = worldPos.x - (scissorWidth * _anchorPoint.x);
            _clippingRect.origin.y = worldPos.y - (scissorHeight * _anchorPoint.y);
            _clippingRect.size.width = scissorWidth;
            _clippingRect.size.height = scissorHeight;
        }
        _clippingRectDirty = false;//置为不可裁切
    }
    return _clippingRect;
}

void Layout::onSizeChanged()
{
    Widget::onSizeChanged();
    setStencilClippingSize(_size);//设置裁切范围
    _doLayoutDirty = true;//开启布局
    _clippingRectDirty = true;//允许计算裁切区域
    if (_backGroundImage)//若背景存在
    {//设置背景位置居中
        _backGroundImage->setPosition(Point(_size.width/2.0f, _size.height/2.0f));
        if (_backGroundScale9Enabled && _backGroundImage)
        {//若使用九宫格 设置透明度
            static_cast<extension::Scale9Sprite*>(_backGroundImage)->setPreferredSize(_size);
        }
    }
    //设置渲染器的渲染区域大小
    if (_colorRender)
    {
        _colorRender->setContentSize(_size);
    }
    if (_gradientRender)
    {
        _gradientRender->setContentSize(_size);
    }
}

void Layout::setBackGroundImageScale9Enabled(bool able)
{
    if (_backGroundScale9Enabled == able)
    {
        return;
    }
    removeProtectedChild(_backGroundImage);
    _backGroundImage = nullptr;//置空背景
    _backGroundScale9Enabled = able;
    addBackGroundImage();//添加背景 设置背景属性
    setBackGroundImage(_backGroundImageFileName,_bgImageTexType);
    setBackGroundImageCapInsets(_backGroundImageCapInsets);
}
    
bool Layout::isBackGroundImageScale9Enabled()
{
    return _backGroundScale9Enabled;
}

void Layout::setBackGroundImage(const std::string& fileName,TextureResType texType)
{
    if (fileName.empty())
    {
        return;
    }
    if (_backGroundImage == nullptr)
    {
        addBackGroundImage();
    }
    _backGroundImageFileName = fileName;
    _bgImageTexType = texType;
    if (_backGroundScale9Enabled)
    {//若使用九宫格 加载资源
        extension::Scale9Sprite* bgiScale9 = static_cast<extension::Scale9Sprite*>(_backGroundImage);
        switch (_bgImageTexType)
        {
            case UI_TEX_TYPE_LOCAL:
                bgiScale9->initWithFile(fileName);
                break;
            case UI_TEX_TYPE_PLIST:
                bgiScale9->initWithSpriteFrameName(fileName);
                break;
            default:
                break;
        }
        bgiScale9->setPreferredSize(_size);
    }
    else
    {//若没有使用九宫格 加载资源
        switch (_bgImageTexType)
        {
            case UI_TEX_TYPE_LOCAL:
                static_cast<Sprite*>(_backGroundImage)->setTexture(fileName);
                break;
            case UI_TEX_TYPE_PLIST:
                static_cast<Sprite*>(_backGroundImage)->setSpriteFrame(fileName);
                break;
            default:
                break;
        }
    }
    //保存贴图size
    _backGroundImageTextureSize = _backGroundImage->getContentSize();
    //设置贴图位置居中
    _backGroundImage->setPosition(Point(_size.width/2.0f, _size.height/2.0f));
    updateBackGroundImageRGBA();//更新贴图颜色
}

void Layout::setBackGroundImageCapInsets(const Rect &capInsets)
{
    _backGroundImageCapInsets = capInsets;
    if (_backGroundScale9Enabled && _backGroundImage)
    {//若使用九宫格 设置区域
        static_cast<extension::Scale9Sprite*>(_backGroundImage)->setCapInsets(capInsets);
    }
}
    
const Rect& Layout::getBackGroundImageCapInsets()
{
    return _backGroundImageCapInsets;
}

void Layout::supplyTheLayoutParameterLackToChild(Widget *child)
{
    if (!child)
    {
        return;
    }
    switch (_layoutType)
    {
        case LAYOUT_ABSOLUTE://绝对位置
            break;
        case LAYOUT_LINEAR_HORIZONTAL:
        case LAYOUT_LINEAR_VERTICAL://线性
        {
            LinearLayoutParameter* layoutParameter = dynamic_cast<LinearLayoutParameter*>(child->getLayoutParameter(LAYOUT_PARAMETER_LINEAR));
            if (!layoutParameter)
            {
                child->setLayoutParameter(LinearLayoutParameter::create());//设置线性布局参数
            }
            break;
        }
        case LAYOUT_RELATIVE://平面
        {
            RelativeLayoutParameter* layoutParameter = dynamic_cast<RelativeLayoutParameter*>(child->getLayoutParameter(LAYOUT_PARAMETER_RELATIVE));
            if (!layoutParameter)
            {
                child->setLayoutParameter(RelativeLayoutParameter::create());//设置平面 布局参数
            }
            break;
        }
        default:
            break;
    }
}

void Layout::addBackGroundImage()
{//背景图添加到节点树上
    if (_backGroundScale9Enabled)
    {
        _backGroundImage = extension::Scale9Sprite::create();
        addProtectedChild(_backGroundImage, BACKGROUNDIMAGE_Z, -1);
        static_cast<extension::Scale9Sprite*>(_backGroundImage)->setPreferredSize(_size);
    }
    else
    {
        _backGroundImage = Sprite::create();
        addProtectedChild(_backGroundImage, BACKGROUNDIMAGE_Z, -1);
    }
    //位置居中
    _backGroundImage->setPosition(Point(_size.width/2.0f, _size.height/2.0f));
}

void Layout::removeBackGroundImage()
{
    if (!_backGroundImage)
    {
        return;
    }//移除背景图 置空背景图及相关属性
    removeProtectedChild(_backGroundImage);
    _backGroundImage = nullptr;
    _backGroundImageFileName = "";
    _backGroundImageTextureSize = Size::ZERO;
}

void Layout::setBackGroundColorType(LayoutBackGroundColorType type)
{
    if (_colorType == type)
    {
        return;
    }
    switch (_colorType)//移除渲染层
    {
        case LAYOUT_COLOR_NONE:
            if (_colorRender)
            {
                removeProtectedChild(_colorRender);
                _colorRender = nullptr;
            }
            if (_gradientRender)
            {
                removeProtectedChild(_gradientRender);
                _gradientRender = nullptr;
            }
            break;
        case LAYOUT_COLOR_SOLID:
            if (_colorRender)
            {
                removeProtectedChild(_colorRender);
                _colorRender = nullptr;
            }
            break;
        case LAYOUT_COLOR_GRADIENT:
            if (_gradientRender)
            {
                removeProtectedChild(_gradientRender);
                _gradientRender = nullptr;
            }
            break;
        default:
            break;
    }
    _colorType = type;//重置渲染层
    switch (_colorType)
    {
        case LAYOUT_COLOR_NONE:
            break;
        case LAYOUT_COLOR_SOLID:
            _colorRender = LayerColor::create();
            _colorRender->setContentSize(_size);
            _colorRender->setOpacity(_cOpacity);
            _colorRender->setColor(_cColor);
            addProtectedChild(_colorRender, BCAKGROUNDCOLORRENDERER_Z, -1);
            break;
        case LAYOUT_COLOR_GRADIENT:
            _gradientRender = LayerGradient::create();
            _gradientRender->setContentSize(_size);
            _gradientRender->setOpacity(_cOpacity);
            _gradientRender->setStartColor(_gStartColor);
            _gradientRender->setEndColor(_gEndColor);
            _gradientRender->setVector(_alongVector);
            addProtectedChild(_gradientRender, BCAKGROUNDCOLORRENDERER_Z, -1);
            break;
        default:
            break;
    }
}
    
LayoutBackGroundColorType Layout::getBackGroundColorType()
{
    return _colorType;
}

void Layout::setBackGroundColor(const Color3B &color)
{
    _cColor = color;
    if (_colorRender)
    {
        _colorRender->setColor(color);
    }
}
    
const Color3B& Layout::getBackGroundColor()
{
    return _cColor;
}

void Layout::setBackGroundColor(const Color3B &startColor, const Color3B &endColor)
{
    _gStartColor = startColor;
    if (_gradientRender)
    {
        _gradientRender->setStartColor(startColor);//开始颜色
    }
    _gEndColor = endColor;
    if (_gradientRender)
    {
        _gradientRender->setEndColor(endColor);//结束颜色
    }
}
    
const Color3B& Layout::getBackGroundStartColor()
{
    return _gStartColor;
}

const Color3B& Layout::getBackGroundEndColor()
{
    return _gEndColor;
}

void Layout::setBackGroundColorOpacity(GLubyte opacity)
{
    _cOpacity = opacity;
    switch (_colorType)
    {
        case LAYOUT_COLOR_NONE:
            break;
        case LAYOUT_COLOR_SOLID://单一的颜色透明度
            _colorRender->setOpacity(opacity);
            break;
        case LAYOUT_COLOR_GRADIENT:
            _gradientRender->setOpacity(opacity);//渐变的颜色透明度
            break;
        default:
            break;
    }
}
    
GLubyte Layout::getBackGroundColorOpacity()
{
    return _cOpacity;
}

void Layout::setBackGroundColorVector(const Point &vector)
{
    _alongVector = vector;
    if (_gradientRender)
    {
        _gradientRender->setVector(vector);//设置渐变方向
    }
}
    
const Point& Layout::getBackGroundColorVector()
{
    return _alongVector;
}

void Layout::setBackGroundImageColor(const Color3B &color)
{
    _backGroundImageColor = color;
    updateBackGroundImageColor();
}

void Layout::setBackGroundImageOpacity(GLubyte opacity)
{
    _backGroundImageOpacity = opacity;
    updateBackGroundImageOpacity();
}

const Color3B& Layout::getBackGroundImageColor()
{
    return _backGroundImageColor;
}

GLubyte Layout::getBackGroundImageOpacity()
{
    return _backGroundImageOpacity;
}

void Layout::updateBackGroundImageColor()
{
    if (_backGroundImage)
    {
        _backGroundImage->setColor(_backGroundImageColor);
    }
}

void Layout::updateBackGroundImageOpacity()
{
    if (_backGroundImage)
    {
        _backGroundImage->setOpacity(_backGroundImageOpacity);
    }
}

void Layout::updateBackGroundImageRGBA()
{
    if (_backGroundImage)
    {//设置背景color opacity
        _backGroundImage->setColor(_backGroundImageColor);
        _backGroundImage->setOpacity(_backGroundImageOpacity);
    }
}

const Size& Layout::getBackGroundImageTextureSize() const
{
    return _backGroundImageTextureSize;//背景资源size
}

void Layout::setLayoutType(LayoutType type)
{
    _layoutType = type;//设置布局类型
    CC_SAFE_RELEASE_NULL(_curLayoutExecutant);//释放布局执行者
    _curLayoutExecutant = createCurrentLayoutExecutant();//新建当前布局执行者
    CC_SAFE_RETAIN(_curLayoutExecutant);//引用次数++
    for (auto& child : _children)//遍历所有孩子
    {
        Widget* widgetChild = dynamic_cast<Widget*>(child);
        if (widgetChild)
        {
            //提供布局参数
            supplyTheLayoutParameterLackToChild(static_cast<Widget*>(child));
        }
    }
    _doLayoutDirty = true;//开启布局
}
    
LayoutExecutant* Layout::createCurrentLayoutExecutant()
{
    LayoutExecutant* exe = nullptr;
    switch (_layoutType)
    {
        case LAYOUT_LINEAR_VERTICAL://线性垂直
            exe = LinearVerticalLayoutExecutant::create();
            break;
        case LAYOUT_LINEAR_HORIZONTAL://线性水平
            exe = LinearHorizontalLayoutExecutant::create();
            break;
        case LAYOUT_RELATIVE://平面
            exe = RelativeLayoutExecutant::create();
            break;
        default:
            break;
    }
    return exe;
}

LayoutType Layout::getLayoutType() const
{
    return _layoutType;//返回布局类型
}
    //请求布局
void Layout::requestDoLayout()
{
    _doLayoutDirty = true;//开启布局
}

void Layout::doLayout()
{
    if (!_doLayoutDirty)//没开启布局 直接return
    {
        return;
    }
    if (_curLayoutExecutant)//开启布局 有了布局数据
    {
        //布局吧
        _curLayoutExecutant->doLayout(getSize(), getChildren());
    }
    _doLayoutDirty = false;//布局完成
}

std::string Layout::getDescription() const
{
    return "Layout";
}

Widget* Layout::createCloneInstance()
{
    return Layout::create();
}

void Layout::copyClonedWidgetChildren(Widget* model)
{
    Widget::copyClonedWidgetChildren(model);
}
//克隆属性
void Layout::copySpecialProperties(Widget *widget)
{
    Layout* layout = dynamic_cast<Layout*>(widget);
    if (layout)
    {
        setBackGroundImageScale9Enabled(layout->_backGroundScale9Enabled);
        setBackGroundImage(layout->_backGroundImageFileName,layout->_bgImageTexType);
        setBackGroundImageCapInsets(layout->_backGroundImageCapInsets);
        setBackGroundColorType(layout->_colorType);
        setBackGroundColor(layout->_cColor);
        setBackGroundColor(layout->_gStartColor, layout->_gEndColor);
        setBackGroundColorOpacity(layout->_cOpacity);
        setBackGroundColorVector(layout->_alongVector);
        setLayoutType(layout->_layoutType);
        setClippingEnabled(layout->_clippingEnabled);
        setClippingType(layout->_clippingType);
    }
}
}
NS_CC_END