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

    xiaoxiao2022-07-03  108

    对应源码位置: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的实现。

    最新回复(0)