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