KKGLinearLayout.cpp 13.3 KB
//
//  KKGLinearLayout.cpp
//  SteveAndMaggieWorld
//
//  Created by Katarzyna Kalinowska-Górska on 03/12/2020.
//

#include "KKGLinearLayout.h"

#define CONTAINER_Z_ORDER 10
#define BACKGROUND_Z_ORDER 0

KKGLinearLayout* KKGLinearLayout::create(LayoutType type, Alignment alignment, Gravity gravity){
    KKGLinearLayout *ret = new (std::nothrow) KKGLinearLayout();
    if (ret->init(type, alignment, gravity))
    {
        ret->autorelease();
        return ret;
    }
    CC_SAFE_DELETE(ret);
    return nullptr;
}

bool KKGLinearLayout::init(LayoutType type, Alignment alignment, Gravity gravity){
    if(!cocos2d::Node::init()){
//    if(!cocos2d::LayerColor::initWithColor(cocos2d::Color4B(220, 0, 00, 250))){
        return false;
    }
    
    m_type = type;
    m_alignment = alignment;
    m_gravity = gravity;
    prepareInternalContainer();
    
    return true;
}


void KKGLinearLayout::setCascadeOpacityEnabled(bool cascadeOpacityEnabled){
    cocos2d::Node::setCascadeOpacityEnabled(cascadeOpacityEnabled);
    m_internalContainer->setCascadeOpacityEnabled(cascadeOpacityEnabled);
}

void KKGLinearLayout::setInterViewMargin(float margin, bool immediatelyRelayout){
    m_interChildMargin = margin;
    if(immediatelyRelayout){
        layoutViews();
    }
}

float KKGLinearLayout::getInterViewMargin(){
    return m_interChildMargin;
}

void KKGLinearLayout::addViews(std::vector<cocos2d::Node*> views, bool immediatelyRelayout){
    auto currIndex = (int)m_internalContainer->getChildrenCount();
    for(auto it = views.begin(); it != views.end(); ++it){
        (*it)->setTag(currIndex++);
        m_internalContainer->addChild(*it);
    }
    if(immediatelyRelayout){
        layoutViews();
    }
}

void KKGLinearLayout::setLayoutContentSize(const cocos2d::Size & size, bool immediatelyRelayout){
    cocos2d::Node::setContentSize(size);
    if(immediatelyRelayout){
        layoutViews();
    }
}

const cocos2d::Size& KKGLinearLayout::getLayoutContentSize() const {
    return cocos2d::Node::getContentSize();
}

void KKGLinearLayout::addView(cocos2d::Node * view, bool immediatelyRelayout){

    view->setTag((int)m_internalContainer->getChildrenCount());
    m_internalContainer->addChild(view);
    
    if(immediatelyRelayout){
        layoutViews();
    }
}

void KKGLinearLayout::setBackgroundColor(const cocos2d::Color4B& bgColor){
    if(m_backgroundNode != nullptr){
        m_backgroundNode->removeFromParent();
    }
    m_backgroundNode = cocos2d::LayerColor::create(bgColor);
    addChild(m_backgroundNode);
    m_backgroundNode->setPosition(cocos2d::Vec2::ZERO);
    adjustBackground(getContentSize());
    m_backgroundNode->setLocalZOrder(BACKGROUND_Z_ORDER);
}

void KKGLinearLayout::substituteViewAtIndex(int index, cocos2d::Node * newView){
    CCASSERT(index >= 0 && index < m_internalContainer->getChildrenCount(), "KKGLinearLayout::substituteViewAtIndex: invalid index");
    auto oldView = m_internalContainer->getChildByTag(index);
    newView->setPosition(oldView->getPosition());
    newView->setAnchorPoint(oldView->getAnchorPoint());
    newView->setContentSize(oldView->getContentSize());
    newView->setTag(index);
    oldView->removeFromParent();
    m_internalContainer->addChild(newView);
}

void KKGLinearLayout::layoutViews(){
    
    if(m_internalContainer == nullptr){ //this also means there are no children to layout
        return;
    }
    
    if(m_type == HORIZONTAL){
        
        auto childrenTotalWidth = 0;
        auto childrenMaxHeight = 0;
        auto allChildren = m_internalContainer->getChildren();
        for(int i = 0; i < allChildren.size(); ++i){
//        for(auto it = allChildren.begin(); it != allChildren.end(); ++it){
//            auto child = *it;
            auto child = m_internalContainer->getChildByTag(i);
            if(child != nullptr){
                childrenMaxHeight = MAX(child->getBoundingBox().size.height, childrenMaxHeight);
                childrenTotalWidth += child->getBoundingBox().size.width;
                childrenTotalWidth += m_interChildMargin;
            }
        }
        
        childrenTotalWidth -= m_interChildMargin;
        m_internalContainer->setContentSize(cocos2d::Size(childrenTotalWidth, childrenMaxHeight));
        
        cocos2d::Point internalContainerPosition { cocos2d::Point::ZERO };
        float childAnchorPointY = 0;
        float childPositionY = 0;
        
        switch(m_alignment){
            case ASTART:
                m_internalContainerAnchorPoint.x = 0;// = (cocos2d::Vec2(0, 0.5));
                internalContainerPosition.x = 0;// = cocos2d::Vec2(0, getContentSize().height/2);
                break;
            case AEND:
                m_internalContainerAnchorPoint.x = 1;
                internalContainerPosition.x = getContentSize().width;
//                m_internalContainer->setAnchorPoint(cocos2d::Vec2(1, 0.5));
//                m_internalContainer->setPosition(getContentSize().width, getContentSize().height/2);
                break;
            case ACENTER:
                m_internalContainerAnchorPoint.x = 0.5;
                internalContainerPosition.x = getContentSize().width/2;
//                m_internalContainer->setAnchorPoint(cocos2d::Vec2(0.5, 0.5));
//                m_internalContainer->setPosition(getContentSize()/2);
                break;
        }
        
        switch(m_gravity){
            case GSTART:
                m_internalContainerAnchorPoint.y = 1;
                internalContainerPosition.y = getContentSize().height;
                childAnchorPointY = 0;
                childPositionY = 0;
                break;
            case GEND:
                m_internalContainerAnchorPoint.y = 0;
                internalContainerPosition.y = 0;
                childAnchorPointY = 1;
                childPositionY = childrenMaxHeight;
                break;
            case GCENTER:
                m_internalContainerAnchorPoint.y = 0.5;
                internalContainerPosition.y = getContentSize().height/2;
                childAnchorPointY = 0.5;
                childPositionY = childrenMaxHeight / 2;
                break;
        }
        
        m_internalContainer->setAnchorPoint(m_internalContainerAnchorPoint);
        m_internalContainer->setPosition(internalContainerPosition);
        
        float currentPoint = 0;
        for(int i = 0; i < allChildren.size(); ++i){
//        for(auto it = allChildren.begin(); it != allChildren.end(); ++it){
//            auto child = *it;
            auto child = m_internalContainer->getChildByTag(i);
            if(child != nullptr){
                child->setAnchorPoint(cocos2d::Vec2(0, childAnchorPointY));
                child->setPosition(cocos2d::Vec2(currentPoint, childPositionY));
                currentPoint += child->getBoundingBox().size.width + m_interChildMargin;
            }
        }
        
    } else if(m_type == VERTICAL){
        
        auto childrenTotalHeight = 0;
        auto childrenMaxWidth = 0;
        auto allChildren = m_internalContainer->getChildren();
        for(int i = 0; i < allChildren.size(); ++i){
//        for(auto it = allChildren.begin(); it != allChildren.end(); ++it){
//            auto child = *it;
            auto child = m_internalContainer->getChildByTag(i);
            if(child != nullptr){
                childrenMaxWidth = MAX(child->getBoundingBox().size.width, childrenMaxWidth);
                childrenTotalHeight += child->getBoundingBox().size.height;
                childrenTotalHeight += m_interChildMargin;
            }
        }
        
        childrenTotalHeight -= m_interChildMargin;
        
        m_internalContainer->setContentSize(cocos2d::Size(childrenMaxWidth, childrenTotalHeight));
        
        cocos2d::Point internalContainerPosition { cocos2d::Point::ZERO };
        float childAnchorPointX = 0;
        float childPositionX = 0;
        
        switch(m_alignment){
            case ASTART:
                m_internalContainerAnchorPoint.y = 1;
                internalContainerPosition.y = getContentSize().height;
                break;
            case AEND:
                m_internalContainerAnchorPoint.y = 0;
                internalContainerPosition.y = 0;
//                m_internalContainer->setAnchorPoint(cocos2d::Vec2(1, 0.5));
//                m_internalContainer->setPosition(getContentSize().width, getContentSize().height/2);
                break;
            case ACENTER:
                m_internalContainerAnchorPoint.y = 0.5;
                internalContainerPosition.y = getContentSize().height/2;
//                m_internalContainer->setAnchorPoint(cocos2d::Vec2(0.5, 0.5));
//                m_internalContainer->setPosition(getContentSize()/2);
                break;
        }
        
        switch(m_gravity){
            case GSTART:
                m_internalContainerAnchorPoint.x = 0;
                internalContainerPosition.x = 0;
                childAnchorPointX = 0;
                childPositionX = 0;
                break;
            case GEND:
                m_internalContainerAnchorPoint.x = 1;
                internalContainerPosition.x = getContentSize().width;
                childAnchorPointX = 1;
                childPositionX = childrenMaxWidth;
                break;
            case GCENTER:
                m_internalContainerAnchorPoint.x = 0.5;
                internalContainerPosition.x = getContentSize().width/2;
                childAnchorPointX = 0.5;
                childPositionX = childrenMaxWidth/2;
                break;
        }
        
        m_internalContainer->setAnchorPoint(m_internalContainerAnchorPoint);
        m_internalContainer->setPosition(internalContainerPosition);
        
        float currentPoint = childrenTotalHeight;
        
        for(int i = 0; i < allChildren.size(); ++i){
//        for(auto it = allChildren.begin(); it != allChildren.end(); ++it){
//            auto child = *it;
            auto child = m_internalContainer->getChildByTag(i);
            if(child != nullptr){
                if(child->getRotation() == 0){ //TODO: handle rotated children
                    child->setAnchorPoint(cocos2d::Vec2(childAnchorPointX, 1));
                } else if(child->getRotation() == 90) {
                    child->setAnchorPoint(cocos2d::Vec2(0, childAnchorPointX));
                }   
                child->setPosition(cocos2d::Vec2(childPositionX, currentPoint));
                currentPoint -= child->getBoundingBox().size.height + m_interChildMargin;
            }
        }
                
//        switch(m_gravity){
//            case START:
//                m_internalContainer->setAnchorPoint(cocos2d::Vec2(0.5, 1));
//                m_internalContainer->setPosition(getContentSize().width/2, getContentSize().height);
//                break;
//            case END:
//                m_internalContainer->setAnchorPoint(cocos2d::Vec2(0.5, 0));
//                m_internalContainer->setPosition(getContentSize().width/2, 0);
//                break;
//            case CENTER:
//                m_internalContainer->setAnchorPoint(cocos2d::Vec2(0.5, 0.5));
//                m_internalContainer->setPosition(getContentSize()/2);
//                break;
//        }
        
    }
    
    adjustBackground(getContentSize());
}

cocos2d::Vector<cocos2d::Node*> KKGLinearLayout::getViews(){
    return m_internalContainer->getChildren();
}

cocos2d::Node* KKGLinearLayout::getView(int index){
    return m_internalContainer->getChildByTag(index);
}

bool KKGLinearLayout::hasViews(){
    return m_internalContainer != nullptr && m_internalContainer->getChildrenCount() > 0;
}

void KKGLinearLayout::removeAllViews(bool relayout){
    m_internalContainer->removeAllChildren();
    if(relayout){
        layoutViews();
    }
}

void KKGLinearLayout::removeLastView(bool relayout){
    auto childrenCount = m_internalContainer->getChildrenCount();
    assert(0 < childrenCount);
    auto child = m_internalContainer->getChildByTag(childrenCount - 1);
    if(child != nullptr){
        child->removeFromParent();
        if(relayout){
            layoutViews();
        }
    }
}

//void KKGLinearLayout::removeViewAtIndex(int index, bool relayout){
//    assert(index < m_internalContainer->getChildrenCount());
//    auto view = m_internalContainer->getChildByTag(index);
//    view->removeFromParent();
//    if(relayout){
//        layoutViews();
//    }
//    
//    //TODO: retag children
//}

void KKGLinearLayout::wrapContents(){
    layoutViews();
    if(m_internalContainer == nullptr){
        return;
    }
    auto newContentSize = m_internalContainer->getContentSize();
    setContentSize(newContentSize);
    m_internalContainer->setPosition(newContentSize.width * m_internalContainerAnchorPoint.x, newContentSize.height*m_internalContainerAnchorPoint.y);    //TODO separate method adjust inner container position
    adjustBackground(newContentSize);
}

void KKGLinearLayout::prepareInternalContainer(){
//    if(m_internalContainer == nullptr){
    m_internalContainer = cocos2d::Node::create();//cocos2d::LayerColor::create(cocos2d::Color4B(100, 200, 100, 250));
    m_internalContainer->setIgnoreAnchorPointForPosition(false);
    addChild(m_internalContainer);
    m_internalContainer->setLocalZOrder(CONTAINER_Z_ORDER);
//    }
}

void KKGLinearLayout::adjustBackground(const cocos2d::Size& contentSize){
    if(m_backgroundNode != nullptr){
        m_backgroundNode->setContentSize(contentSize);
//        m_backgroundNode->setPosition(contentSize/2);
    }
}