10.【cocos2d-x 源码分析】:UI系统的详细分析(上)

对应源码位置:cocos2d-x-3.3\cocos\ui\UI*
Widget 看起

先从 一些布局的基本讲起

//这里是 代表边距的类
class CC_GUI_DLL Margin
{
public:
    float left;
    float top;
    float right;
    float bottom;
   
public:
    Margin();
    Margin(float l, float t, float r, float b);
    Margin(const Margin& other);
    Margin& operator= (const Margin& other);
    void setMargin(float l, float t, float r, float b);
    bool equals(const Margin& target) const;
    static const Margin ZERO;
};
//LayoutParameter 布局的参数 只有Margin参数
class CC_GUI_DLL LayoutParameter : public Ref
{
public:
//绝对布局 与 相对布局
    enum class Type
    {
        NONE = 0,
        LINEAR,
        RELATIVE
    };
    LayoutParameter() : _margin(Margin())
    {
        _layoutParameterType = Type::NONE;
    };
    
    virtual ~LayoutParameter(){};

    static LayoutParameter* create();
   
    void setMargin(const Margin& margin);
  
    const Margin& getMargin() const;
  
    Type getLayoutType() const;
    
    LayoutParameter* clone();
    virtual LayoutParameter* createCloneInstance();
    virtual void copyProperties(LayoutParameter* model);
protected:
    Margin _margin;
    Type _layoutParameterType;
};

下面看看 所谓的 相对布局绝对布局

class CC_GUI_DLL LinearLayoutParameter : public LayoutParameter
{
public:
	//线性布局的重力 
	//跟android的布局文件xml写法类似
    enum class LinearGravity
    {
        NONE,
        LEFT,
        TOP,
        RIGHT,
        BOTTOM,
        CENTER_VERTICAL,
        CENTER_HORIZONTAL
    };
   
    LinearLayoutParameter()
    : _linearGravity(LinearGravity::NONE)
    {
        _layoutParameterType = Type::LINEAR;
    };

    static LinearLayoutParameter* create();
 
    virtual LayoutParameter* createCloneInstance() override;
    virtual void copyProperties(LayoutParameter* model) override;
protected:
//多了一个 重力方向的 参数
    LinearGravity _linearGravity;
};
//看看 相对布局
class CC_GUI_DLL RelativeLayoutParameter : public LayoutParameter
{
public:
	//对齐方式
    enum class RelativeAlign
    {
    	//相对于 父节点
        NONE,
        PARENT_TOP_LEFT,
        PARENT_TOP_CENTER_HORIZONTAL,
        PARENT_TOP_RIGHT,
        PARENT_LEFT_CENTER_VERTICAL,
        
        CENTER_IN_PARENT,
        
        PARENT_RIGHT_CENTER_VERTICAL,
        PARENT_LEFT_BOTTOM,
        PARENT_BOTTOM_CENTER_HORIZONTAL,
        PARENT_RIGHT_BOTTOM,
        //相对其他 组件的位置
        LOCATION_ABOVE_LEFTALIGN,
        LOCATION_ABOVE_CENTER,
        LOCATION_ABOVE_RIGHTALIGN,
        LOCATION_LEFT_OF_TOPALIGN,
        LOCATION_LEFT_OF_CENTER,
        LOCATION_LEFT_OF_BOTTOMALIGN,
        LOCATION_RIGHT_OF_TOPALIGN,
        LOCATION_RIGHT_OF_CENTER,
        LOCATION_RIGHT_OF_BOTTOMALIGN,
        LOCATION_BELOW_LEFTALIGN,
        LOCATION_BELOW_CENTER,
        LOCATION_BELOW_RIGHTALIGN
    };
    /**
     * Default constructor
     */
    RelativeLayoutParameter()
    : _relativeAlign(RelativeAlign::NONE),
    _relativeWidgetName(""),
    _relativeLayoutName(""),
    _put(false)
    {
        _layoutParameterType = Type::RELATIVE;
    };
    
    static RelativeLayoutParameter* create();
    
    void setAlign(RelativeAlign align);
    //相对于 那个组件
    void setRelativeToWidgetName(const std::string& name);
    
    const std::string& getRelativeToWidgetName() const;
    //给自己起个名字
    void setRelativeName(const std::string& name);
    const std::string& getRelativeName() const;
    
    virtual LayoutParameter* createCloneInstance() override;
    virtual void copyProperties(LayoutParameter* model) override;
protected:
	//对其类型
    RelativeAlign _relativeAlign;
    //相对于的 组件的名字
    std::string _relativeWidgetName;
    //自己的名字
    std::string _relativeLayoutName;
    bool _put;
    friend class RelativeLayoutManager;
};
//最后 加了一个 获取 LayoutParameter的接口
class CC_GUI_DLL LayoutParameterProtocol
{
public:
    virtual ~LayoutParameterProtocol(){}
    virtual LayoutParameter* getLayoutParameter() const= 0;
};

正式开始看看 Widget

class CC_GUI_DLL Widget : public ProtectedNode, public LayoutParameterProtocol
{
public:
	//焦点方向
    enum class FocusDirection
    {
        LEFT,
        RIGHT,
        UP,
        DOWN
    };
    //位置 数据类型
    enum class PositionType
    {
        ABSOLUTE,
        PERCENT
    };
    //尺寸类型
    enum class SizeType
    {
        ABSOLUTE,
        PERCENT
    };
    //事件类型 见惯不怪
    enum class TouchEventType
    {
        BEGAN,
        MOVED,
        ENDED,
        CANCELED
    };
    //纹理类型
    enum class TextureResType
    {
        LOCAL = 0,
        PLIST = 1
    };
    //明亮程度
    enum class BrightStyle
    {
        NONE = -1,
        NORMAL,
        HIGHLIGHT
    };
	//回调
    typedef std::function<void(Ref*,Widget::TouchEventType)> ccWidgetTouchCallback;
    typedef std::function<void(Ref*)> ccWidgetClickCallback;
    typedef std::function<void(Ref*, int)> ccWidgetEventCallback;
  
    float getLeftBoundary() const;

    float getBottomBoundary() const;

    float getRightBoundary() const;

    float getTopBoundary() const;

    virtual void visit(cocos2d::Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags) override;

    /**
     * Changes the position (x,y) of the widget in OpenGL coordinates
     *
     * Usually we use p(x,y) to compose Vec2 object.
     * The original point (0,0) is at the left-bottom corner of screen.
     *
     * @param position  The position (x,y) of the widget in OpenGL coordinates
     */
     //在opengl的坐标系下 0,0 代表 
    virtual void setPosition(const Vec2 &pos) override;
	//使用百分比
    void setPositionPercent(const Vec2 &percent);

    bool isClippingParentContainsPoint(const Vec2& pt);

    virtual void setContentSize(const Size& contentSize) override;

    virtual void setSizePercent(const Vec2 &percent);
	//用于判断某个点 是否在本区域
    virtual bool hitTest(const Vec2 &pt);
	//回调
    virtual bool onTouchBegan(Touch *touch, Event *unusedEvent);
    virtual void onTouchMoved(Touch *touch, Event *unusedEvent);
    virtual void onTouchEnded(Touch *touch, Event *unusedEvent);
    virtual void onTouchCancelled(Touch *touch, Event *unusedEvent);

    void setLayoutParameter(LayoutParameter* parameter);
	//是否忽略自定义的size
    virtual void ignoreContentAdaptWithSize(bool ignore);
    //虚拟的渲染器
    virtual Node* getVirtualRenderer();
    virtual Size getVirtualRendererSize() const;
	//是否 传递触摸事件给父级节点
    void setPropagateTouchEvents(bool isPropagate); 
   //是否吞噬事件
    void setSwallowTouches(bool swallow);
     //如果在一个布局中 则找到下一个widget  若不在布局中 则返回自己
    virtual Widget* findNextFocusedWidget(FocusDirection direction, Widget* current);
    //获取焦点
    void requestFocus();
	//获取 当前聚焦的widget
    Widget* getCurrentFocusedWidget()const;
 
    std::function<void(Widget*,Widget*)> onFocusChanged; 
    std::function<Widget*(FocusDirection)> onNextFocusedWidget;
   
protected:
    //一堆callback被省略了
    void cleanupWidget();
    LayoutComponent* getOrCreateLayoutComponent();

protected:
    bool _unifySize;
    bool _enabled;
    bool _bright;
    bool _touchEnabled;
    bool _highlight;
    bool _affectByClipping;
    bool _ignoreSize;
    bool _propagateTouchEvents;

    BrightStyle _brightStyle;
    SizeType _sizeType;
    PositionType _positionType;

    //used for search widget by action tag in UIHelper class
    int _actionTag;

    Size _customSize;

    Vec2 _sizePercent;
    Vec2 _positionPercent;

    bool _hitted;
    EventListenerTouchOneByOne* _touchListener;
    Vec2 _touchBeganPosition;
    Vec2 _touchMovePosition;
    Vec2 _touchEndPosition;

    bool _flippedX;
    bool _flippedY;

    //use map to enble switch back and forth for user layout parameters
    Map<int,LayoutParameter*> _layoutParameterDictionary;
    LayoutParameter::Type _layoutParameterType;

    bool _focused;
    bool _focusEnabled;
   //唯一的 focus对象  全局静态的
    static Widget *_focusedWidget;  //both layout & widget will be stored in this variable

    Ref*       _touchEventListener;
   //回调
    ccWidgetTouchCallback _touchEventCallback;
    ccWidgetClickCallback _clickEventListener;
    ccWidgetEventCallback _ccEventCallback;
    
    std::string _callbackType;
    std::string _callbackName;
private:
    class FocusNavigationController;
    //用于监听 键盘的方向键  处理焦点对象 的绑定
    static FocusNavigationController* _focusNavigationController;
};}
//当该控件 焦点改变  则默认 想不通方向 选择焦点控件
void Widget::FocusNavigationController::onKeypadKeyPressed(EventKeyboard::KeyCode  keyCode, Event *event)
{
    if (_enableFocusNavigation && _firstFocusedWidget)
    {
        if (keyCode == EventKeyboard::KeyCode::KEY_DPAD_DOWN)
        {
        	//这个函数 用于找到 方向的下一个空间 后面再讲
            _firstFocusedWidget = _firstFocusedWidget->findNextFocusedWidget(Widget::FocusDirection::DOWN, _firstFocusedWidget);
        }
        if (keyCode == EventKeyboard::KeyCode::KEY_DPAD_UP)
        {
            _firstFocusedWidget = _firstFocusedWidget->findNextFocusedWidget(Widget::FocusDirection::UP, _firstFocusedWidget);
        }
        if (keyCode == EventKeyboard::KeyCode::KEY_DPAD_LEFT)
        {
            _firstFocusedWidget = _firstFocusedWidget->findNextFocusedWidget(Widget::FocusDirection::LEFT, _firstFocusedWidget);
        }
        if (keyCode == EventKeyboard::KeyCode::KEY_DPAD_RIGHT)
        {
            _firstFocusedWidget = _firstFocusedWidget->findNextFocusedWidget(Widget::FocusDirection::RIGHT, _firstFocusedWidget);
        }
    }
}

看看具体实现

Widget* Widget::create()
{
    Widget* widget = new (std::nothrow) Widget();
    if (widget && widget->init())
    {
        widget->autorelease();
        return widget;
    }
    CC_SAFE_DELETE(widget);
    return nullptr;
}

bool Widget::init()
{
    if (ProtectedNode::init())
    {	
    	//空函数
        initRenderer();
        setBright(true);
        onFocusChanged = CC_CALLBACK_2(Widget::onFocusChange,this);
        onNextFocusedWidget = nullptr;
        this->setAnchorPoint(Vec2(0.5f, 0.5f));
        //即为设置 大小为为 ContentSize 而非customsize 自适应
        ignoreContentAdaptWithSize(true);
        return true;
    }
    return false;
}
//visit 目前没有特殊处理 简单的遍历操作。
void Widget::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
    if (_visible || !isVisitableByVisitingCamera())
    {
        adaptRenderers();
        ProtectedNode::visit(renderer, parentTransform, parentFlags);
    }
}
//获取父级控件
Widget* Widget::getAncensterWidget(Node* node)
{
    if (nullptr == node)
    {
        return nullptr;
    }
    
    Node* parent = node->getParent();
    if (nullptr == parent)
    {
        return nullptr;
    }
    //看是否 是 Widget类型
    Widget* parentWidget = dynamic_cast<Widget*>(parent);
    if (parentWidget)
    {
        return parentWidget;
    }
    else
    {
        return this->getAncensterWidget(parent->getParent());
    }
}
   //就是看 父级是否可见
bool Widget::isAncestorsVisible(Node* node)
{
    if (nullptr == node)
    {
        return true;
    }
    Node* parent = node->getParent();
    
    if (parent && !parent->isVisible())
    {
        return false;
    }
    return this->isAncestorsVisible(parent);
}
//判断是否在本对象的区域内
bool Widget::hitTest(const Vec2 &pt)
{
	//转换为 Node Space
    Vec2 nsp = convertToNodeSpace(pt);
    Rect bb;
    bb.size = _contentSize;
    if (bb.containsPoint(nsp))
    {
        return true;
    }
    return false;
}
//判断 是否在父级控件的点击范围  一个个往上层找 直到没了
bool Widget::isClippingParentContainsPoint(const Vec2 &pt)
{
    _affectByClipping = false;
    Widget* parent = getWidgetParent();
    Widget* clippingParent = nullptr;
    while (parent)
    {
        Layout* layoutParent = dynamic_cast<Layout*>(parent);
        if (layoutParent)
        {
            if (layoutParent->isClippingEnabled())
            {
                _affectByClipping = true;
                clippingParent = layoutParent;
                break;
            }
        }
        parent = parent->getWidgetParent();
    }

    if (!_affectByClipping)
    {
        return true;
    }


    if (clippingParent)
    {
        bool bRet = false;
        if (clippingParent->hitTest(pt))
        {
            bRet = true;
        }
        if (bRet)
        {
            return clippingParent->isClippingParentContainsPoint(pt);
        }
        return false;
    }
    return true;
}
//下面是控件的点击处理
bool Widget::onTouchBegan(Touch *touch, Event *unusedEvent)
{
    _hitted = false;
    if (isVisible() && isEnabled() && isAncestorsEnabled() && isAncestorsVisible(this) )
    {
        _touchBeganPosition = touch->getLocation();
        //如果 在自己的区域内 也在父节点的区域内  才处理
        if(hitTest(_touchBeganPosition) && isClippingParentContainsPoint(_touchBeganPosition))
        {
            _hitted = true;
        }
    }
    if (!_hitted)
    {
        return false;
    }
    setHighlighted(true);
    
    /*
     * Propagate touch events to its parents
     */
    if (_propagateTouchEvents)
    {
        this->propagateTouchEvent(TouchEventType::BEGAN, this, touch);
    }
  
    pushDownEvent();
    return true;
}
最后

下一篇分析 Layout的实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值