mirror of https://github.com/axmolengine/axmol.git
RichText and ScrollView enhancements (#1696)
* Add id tag to several RichText elements to allow locating the nodes in RichText Add font related styling to paragraph tags * Move functionality out of ListView and into ScrollView to allow scrolling to a specific child node within a ScrollView * Add function to allow finding protected child node by name * Example of anchor tags to local RichText content in a ScrollView * Fix string storage type
This commit is contained in:
parent
8e2e577e2f
commit
978d15db78
|
@ -128,6 +128,17 @@ Node* ProtectedNode::getProtectedChildByTag(int tag)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Node* ProtectedNode::getProtectedChildByName(std::string_view name)
|
||||
{
|
||||
// AXASSERT(!name.empty(), "Invalid name");
|
||||
for (auto&& child : _protectedChildren)
|
||||
{
|
||||
if (child && child->getName() == name)
|
||||
return child;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* "remove" logic MUST only be on this method
|
||||
* If a class want's to extend the 'removeChild' behavior it only needs
|
||||
* to override this method
|
||||
|
|
|
@ -94,6 +94,15 @@ public:
|
|||
*/
|
||||
virtual Node* getProtectedChildByTag(int tag);
|
||||
|
||||
/**
|
||||
* Gets a child from the container with its name.
|
||||
*
|
||||
* @param name An identifier to find the child node.
|
||||
*
|
||||
* @return a Node object whose name equals to the input parameter.
|
||||
*/
|
||||
virtual Node* getProtectedChildByName(std::string_view name);
|
||||
|
||||
////// REMOVES //////
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/****************************************************************************
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md).
|
||||
|
||||
https://axmolengine.github.io/
|
||||
|
||||
|
@ -28,8 +29,6 @@ THE SOFTWARE.
|
|||
|
||||
NS_AX_BEGIN
|
||||
|
||||
static const float DEFAULT_TIME_IN_SEC_FOR_SCROLL_TO_ITEM = 1.0f;
|
||||
|
||||
namespace ui
|
||||
{
|
||||
|
||||
|
@ -45,7 +44,6 @@ ListView::ListView()
|
|||
, _topPadding(0.0f)
|
||||
, _rightPadding(0.0f)
|
||||
, _bottomPadding(0.0f)
|
||||
, _scrollTime(DEFAULT_TIME_IN_SEC_FOR_SCROLL_TO_ITEM)
|
||||
, _curSelectedIndex(-1)
|
||||
, _innerContainerDoLayoutDirty(true)
|
||||
, _eventCallback(nullptr)
|
||||
|
@ -523,17 +521,6 @@ float ListView::getBottomPadding() const
|
|||
return _bottomPadding;
|
||||
}
|
||||
|
||||
void ListView::setScrollDuration(float time)
|
||||
{
|
||||
if (time >= 0)
|
||||
_scrollTime = time;
|
||||
}
|
||||
|
||||
float ListView::getScrollDuration() const
|
||||
{
|
||||
return _scrollTime;
|
||||
}
|
||||
|
||||
void ListView::setDirection(Direction dir)
|
||||
{
|
||||
switch (dir)
|
||||
|
@ -642,13 +629,6 @@ void ListView::interceptTouchEvent(TouchEventType event, Widget* sender, Touch*
|
|||
}
|
||||
}
|
||||
|
||||
static Vec2 calculateItemPositionWithAnchor(Widget* item, const Vec2& itemAnchorPoint)
|
||||
{
|
||||
Vec2 origin(item->getLeftBoundary(), item->getBottomBoundary());
|
||||
Vec2 size = item->getContentSize();
|
||||
return origin + Vec2(size.width * itemAnchorPoint.x, size.height * itemAnchorPoint.y);
|
||||
}
|
||||
|
||||
static Widget* findClosestItem(const Vec2& targetPosition,
|
||||
const Vector<Widget*>& items,
|
||||
const Vec2& itemAnchorPoint,
|
||||
|
@ -676,7 +656,7 @@ static Widget* findClosestItem(const Vec2& targetPosition,
|
|||
|
||||
// Binary search
|
||||
ssize_t midIndex = (firstIndex + lastIndex) / 2;
|
||||
Vec2 itemPosition = calculateItemPositionWithAnchor(items.at(midIndex), itemAnchorPoint);
|
||||
Vec2 itemPosition = ListView::calculateItemPositionWithAnchor(items.at(midIndex), itemAnchorPoint);
|
||||
float distanceFromMid = (targetPosition - itemPosition).length();
|
||||
if (distanceFromFirst <= distanceFromLast)
|
||||
{
|
||||
|
@ -830,17 +810,6 @@ void ListView::jumpToPercentBothDirection(const Vec2& percent)
|
|||
ScrollView::jumpToPercentBothDirection(percent);
|
||||
}
|
||||
|
||||
Vec2 ListView::calculateItemDestination(const Vec2& positionRatioInView, Widget* item, const Vec2& itemAnchorPoint)
|
||||
{
|
||||
const Vec2& contentSize = getContentSize();
|
||||
Vec2 positionInView;
|
||||
positionInView.x += contentSize.width * positionRatioInView.x;
|
||||
positionInView.y += contentSize.height * positionRatioInView.y;
|
||||
|
||||
Vec2 itemPosition = calculateItemPositionWithAnchor(item, itemAnchorPoint);
|
||||
return -(itemPosition - positionInView);
|
||||
}
|
||||
|
||||
void ListView::jumpToItem(ssize_t itemIndex, const Vec2& positionRatioInView, const Vec2& itemAnchorPoint)
|
||||
{
|
||||
Widget* item = getItem(itemIndex);
|
||||
|
@ -875,8 +844,7 @@ void ListView::scrollToItem(ssize_t itemIndex,
|
|||
{
|
||||
return;
|
||||
}
|
||||
Vec2 destination = calculateItemDestination(positionRatioInView, item, itemAnchorPoint);
|
||||
startAutoScrollToDestination(destination, timeInSec, true);
|
||||
ScrollView::scrollToItem(item, positionRatioInView, itemAnchorPoint, timeInSec);
|
||||
}
|
||||
|
||||
ssize_t ListView::getCurSelectedIndex() const
|
||||
|
@ -920,12 +888,12 @@ void ListView::copyClonedWidgetChildren(Widget* model)
|
|||
}
|
||||
}
|
||||
|
||||
void ListView::copySpecialProperties(Widget* widget)
|
||||
void ListView::copySpecialProperties(Widget* model)
|
||||
{
|
||||
ListView* listViewEx = dynamic_cast<ListView*>(widget);
|
||||
ListView* listViewEx = dynamic_cast<ListView*>(model);
|
||||
if (listViewEx)
|
||||
{
|
||||
ScrollView::copySpecialProperties(widget);
|
||||
ScrollView::copySpecialProperties(model);
|
||||
setItemModel(listViewEx->_model);
|
||||
setItemsMargin(listViewEx->_itemsMargin);
|
||||
setGravity(listViewEx->_gravity);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/****************************************************************************
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md).
|
||||
|
||||
https://axmolengine.github.io/
|
||||
|
||||
|
@ -106,7 +107,7 @@ public:
|
|||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
virtual ~ListView();
|
||||
~ListView() override;
|
||||
|
||||
/**
|
||||
* Create an empty ListView.
|
||||
|
@ -288,31 +289,16 @@ public:
|
|||
*/
|
||||
float getBottomPadding() const;
|
||||
|
||||
/**
|
||||
* Set the time in seconds to scroll between items.
|
||||
* Subsequent calls of function 'scrollToItem', will take 'time' seconds for scrolling.
|
||||
* @param time The seconds needed to scroll between two items. 'time' must be >= 0
|
||||
* @see scrollToItem(ssize_t, const Vec2&, const Vec2&)
|
||||
*/
|
||||
void setScrollDuration(float time);
|
||||
|
||||
/**
|
||||
* Get the time in seconds to scroll between items.
|
||||
* @return The time in seconds to scroll between items
|
||||
* @see setScrollDuration(float)
|
||||
*/
|
||||
float getScrollDuration() const;
|
||||
|
||||
// override methods
|
||||
virtual void doLayout() override;
|
||||
virtual void requestDoLayout() override;
|
||||
virtual void addChild(Node* child) override;
|
||||
virtual void addChild(Node* child, int localZOrder) override;
|
||||
virtual void addChild(Node* child, int zOrder, int tag) override;
|
||||
virtual void addChild(Node* child, int zOrder, std::string_view name) override;
|
||||
virtual void removeAllChildren() override;
|
||||
virtual void removeAllChildrenWithCleanup(bool cleanup) override;
|
||||
virtual void removeChild(Node* child, bool cleanup = true) override;
|
||||
void doLayout() override;
|
||||
void requestDoLayout() override;
|
||||
void addChild(Node* child) override;
|
||||
void addChild(Node* child, int localZOrder) override;
|
||||
void addChild(Node* child, int zOrder, int tag) override;
|
||||
void addChild(Node* child, int zOrder, std::string_view name) override;
|
||||
void removeAllChildren() override;
|
||||
void removeAllChildrenWithCleanup(bool cleanup) override;
|
||||
void removeChild(Node* child, bool cleanup = true) override;
|
||||
|
||||
/**
|
||||
* @brief Query the closest item to a specific position in inner container.
|
||||
|
@ -367,17 +353,17 @@ public:
|
|||
/**
|
||||
* Override functions
|
||||
*/
|
||||
virtual void jumpToBottom() override;
|
||||
virtual void jumpToTop() override;
|
||||
virtual void jumpToLeft() override;
|
||||
virtual void jumpToRight() override;
|
||||
virtual void jumpToTopLeft() override;
|
||||
virtual void jumpToTopRight() override;
|
||||
virtual void jumpToBottomLeft() override;
|
||||
virtual void jumpToBottomRight() override;
|
||||
virtual void jumpToPercentVertical(float percent) override;
|
||||
virtual void jumpToPercentHorizontal(float percent) override;
|
||||
virtual void jumpToPercentBothDirection(const Vec2& percent) override;
|
||||
void jumpToBottom() override;
|
||||
void jumpToTop() override;
|
||||
void jumpToLeft() override;
|
||||
void jumpToRight() override;
|
||||
void jumpToTopLeft() override;
|
||||
void jumpToTopRight() override;
|
||||
void jumpToBottomLeft() override;
|
||||
void jumpToBottomRight() override;
|
||||
void jumpToPercentVertical(float percent) override;
|
||||
void jumpToPercentHorizontal(float percent) override;
|
||||
void jumpToPercentBothDirection(const Vec2& percent) override;
|
||||
|
||||
/**
|
||||
* @brief Jump to specific item
|
||||
|
@ -391,15 +377,20 @@ public:
|
|||
* @brief Scroll to specific item
|
||||
* @param positionRatioInView Specifies the position with ratio in list view's content size.
|
||||
* @param itemAnchorPoint Specifies an anchor point of each item for position to calculate distance.
|
||||
* @param timeInSec Scroll time
|
||||
*/
|
||||
void scrollToItem(ssize_t itemIndex, const Vec2& positionRatioInView, const Vec2& itemAnchorPoint);
|
||||
|
||||
/**
|
||||
* @brief Scroll to specific item
|
||||
* @param positionRatioInView Specifies the position with ratio in list view's content size.
|
||||
* @param itemAnchorPoint Specifies an anchor point of each item for position to calculate distance.
|
||||
* @param timeInSec Scroll time
|
||||
*/
|
||||
void scrollToItem(ssize_t itemIndex, const Vec2& positionRatioInView, const Vec2& itemAnchorPoint, float timeInSec);
|
||||
|
||||
/**
|
||||
* @brief Query current selected widget's index.
|
||||
*
|
||||
|
||||
* @return An index of a selected item.
|
||||
*/
|
||||
ssize_t getCurSelectedIndex() const;
|
||||
|
@ -423,14 +414,14 @@ public:
|
|||
* Direction Direction::VERTICAL means vertical scroll, Direction::HORIZONTAL means horizontal scroll.
|
||||
* @param dir Set the list view's scroll direction.
|
||||
*/
|
||||
virtual void setDirection(Direction dir) override;
|
||||
void setDirection(Direction dir) override;
|
||||
|
||||
virtual std::string getDescription() const override;
|
||||
std::string getDescription() const override;
|
||||
|
||||
virtual bool init() override;
|
||||
bool init() override;
|
||||
|
||||
protected:
|
||||
virtual void handleReleaseLogic(Touch* touch) override;
|
||||
void handleReleaseLogic(Touch* touch) override;
|
||||
|
||||
virtual void onItemListChanged();
|
||||
|
||||
|
@ -439,19 +430,18 @@ protected:
|
|||
void remedyVerticalLayoutParameter(LinearLayoutParameter* layoutParameter, ssize_t itemIndex);
|
||||
void remedyHorizontalLayoutParameter(LinearLayoutParameter* layoutParameter, ssize_t itemIndex);
|
||||
|
||||
virtual void onSizeChanged() override;
|
||||
virtual Widget* createCloneInstance() override;
|
||||
virtual void copySpecialProperties(Widget* model) override;
|
||||
virtual void copyClonedWidgetChildren(Widget* model) override;
|
||||
void onSizeChanged() override;
|
||||
Widget* createCloneInstance() override;
|
||||
void copySpecialProperties(Widget* model) override;
|
||||
void copyClonedWidgetChildren(Widget* model) override;
|
||||
void selectedItemEvent(TouchEventType event);
|
||||
virtual void interceptTouchEvent(Widget::TouchEventType event, Widget* sender, Touch* touch) override;
|
||||
void interceptTouchEvent(Widget::TouchEventType event, Widget* sender, Touch* touch) override;
|
||||
|
||||
virtual Vec2 getHowMuchOutOfBoundary(const Vec2& addition = Vec2::ZERO) override;
|
||||
Vec2 getHowMuchOutOfBoundary(const Vec2& addition = Vec2::ZERO) override;
|
||||
|
||||
virtual void startAttenuatingAutoScroll(const Vec2& deltaMove, const Vec2& initialVelocity) override;
|
||||
void startAttenuatingAutoScroll(const Vec2& deltaMove, const Vec2& initialVelocity) override;
|
||||
|
||||
void startMagneticScroll();
|
||||
Vec2 calculateItemDestination(const Vec2& positionRatioInView, Widget* item, const Vec2& itemAnchorPoint);
|
||||
|
||||
protected:
|
||||
Widget* _model;
|
||||
|
@ -470,8 +460,6 @@ protected:
|
|||
float _rightPadding;
|
||||
float _bottomPadding;
|
||||
|
||||
float _scrollTime;
|
||||
|
||||
ssize_t _curSelectedIndex;
|
||||
|
||||
bool _innerContainerDoLayoutDirty;
|
||||
|
|
|
@ -151,11 +151,12 @@ RichElementText* RichElementText::create(int tag,
|
|||
const Color3B& shadowColor,
|
||||
const Vec2& shadowOffset,
|
||||
int shadowBlurRadius,
|
||||
const Color3B& glowColor)
|
||||
const Color3B& glowColor,
|
||||
std::string_view id)
|
||||
{
|
||||
RichElementText* element = new RichElementText();
|
||||
if (element->init(tag, color, opacity, text, fontName, fontSize, flags, url, outlineColor, outlineSize, shadowColor,
|
||||
shadowOffset, shadowBlurRadius, glowColor))
|
||||
shadowOffset, shadowBlurRadius, glowColor, id))
|
||||
{
|
||||
element->autorelease();
|
||||
return element;
|
||||
|
@ -177,7 +178,8 @@ bool RichElementText::init(int tag,
|
|||
const Color3B& shadowColor,
|
||||
const Vec2& shadowOffset,
|
||||
int shadowBlurRadius,
|
||||
const Color3B& glowColor)
|
||||
const Color3B& glowColor,
|
||||
std::string_view id)
|
||||
{
|
||||
if (RichElement::init(tag, color, opacity))
|
||||
{
|
||||
|
@ -192,6 +194,7 @@ bool RichElementText::init(int tag,
|
|||
_shadowOffset = shadowOffset;
|
||||
_shadowBlurRadius = shadowBlurRadius;
|
||||
_glowColor = glowColor;
|
||||
_id = id;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -202,10 +205,11 @@ RichElementImage* RichElementImage::create(int tag,
|
|||
uint8_t opacity,
|
||||
std::string_view filePath,
|
||||
std::string_view url,
|
||||
Widget::TextureResType texType)
|
||||
Widget::TextureResType texType,
|
||||
std::string_view id)
|
||||
{
|
||||
RichElementImage* element = new RichElementImage();
|
||||
if (element->init(tag, color, opacity, filePath, url, texType))
|
||||
if (element->init(tag, color, opacity, filePath, url, texType, id))
|
||||
{
|
||||
element->autorelease();
|
||||
return element;
|
||||
|
@ -219,7 +223,8 @@ bool RichElementImage::init(int tag,
|
|||
uint8_t opacity,
|
||||
std::string_view filePath,
|
||||
std::string_view url,
|
||||
Widget::TextureResType texType)
|
||||
Widget::TextureResType texType,
|
||||
std::string_view id)
|
||||
{
|
||||
if (RichElement::init(tag, color, opacity))
|
||||
{
|
||||
|
@ -230,6 +235,7 @@ bool RichElementImage::init(int tag,
|
|||
_scaleY = 1.0f;
|
||||
_url = url;
|
||||
_textureType = texType;
|
||||
_id = id;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -250,13 +256,19 @@ void RichElementImage::setUrl(std::string_view url)
|
|||
_url = url;
|
||||
}
|
||||
|
||||
void RichElementImage::setId(std::string_view id)
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
RichElementCustomNode* RichElementCustomNode::create(int tag,
|
||||
const Color3B& color,
|
||||
uint8_t opacity,
|
||||
ax::Node* customNode)
|
||||
ax::Node* customNode,
|
||||
std::string_view id)
|
||||
{
|
||||
RichElementCustomNode* element = new RichElementCustomNode();
|
||||
if (element->init(tag, color, opacity, customNode))
|
||||
if (element->init(tag, color, opacity, customNode, id))
|
||||
{
|
||||
element->autorelease();
|
||||
return element;
|
||||
|
@ -265,11 +277,16 @@ RichElementCustomNode* RichElementCustomNode::create(int tag,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool RichElementCustomNode::init(int tag, const Color3B& color, uint8_t opacity, ax::Node* customNode)
|
||||
bool RichElementCustomNode::init(int tag,
|
||||
const Color3B& color,
|
||||
uint8_t opacity,
|
||||
ax::Node* customNode,
|
||||
std::string_view id)
|
||||
{
|
||||
if (RichElement::init(tag, color, opacity))
|
||||
{
|
||||
_customNode = customNode;
|
||||
_id = id;
|
||||
_customNode->retain();
|
||||
return true;
|
||||
}
|
||||
|
@ -339,6 +356,7 @@ public:
|
|||
Vec2 shadowOffset; /*!< shadow effect offset value */
|
||||
int shadowBlurRadius; /*!< the shadow effect blur radius */
|
||||
Color3B glowColor; /*!< the glow effect color value */
|
||||
std::string name; /*!< the name of this element */
|
||||
|
||||
void setColor(const Color3B& acolor)
|
||||
{
|
||||
|
@ -398,6 +416,8 @@ public:
|
|||
|
||||
std::tuple<bool, Color3B> getGlow() const;
|
||||
|
||||
std::string getName() const;
|
||||
|
||||
void startElement(void* ctx, const char* name, const char** atts) override;
|
||||
|
||||
void endElement(void* ctx, const char* name) override;
|
||||
|
@ -569,6 +589,11 @@ MyXMLVisitor::MyXMLVisitor(RichText* richText) : _fontElements(20), _richText(ri
|
|||
|
||||
elementImg->setScaleX(scaleX);
|
||||
elementImg->setScaleY(scaleY);
|
||||
|
||||
if (tagAttrValueMap.find("id") != tagAttrValueMap.end())
|
||||
{
|
||||
elementImg->setId(tagAttrValueMap.at("id").asString());
|
||||
}
|
||||
}
|
||||
return make_pair(ValueMap(), elementImg);
|
||||
});
|
||||
|
@ -581,6 +606,12 @@ MyXMLVisitor::MyXMLVisitor(RichText* richText) : _fontElements(20), _richText(ri
|
|||
{
|
||||
attrValueMap[RichText::KEY_URL] = tagAttrValueMap.at("href").asString();
|
||||
}
|
||||
|
||||
if (tagAttrValueMap.find("id") != tagAttrValueMap.end())
|
||||
{
|
||||
attrValueMap[RichText::KEY_ID] = tagAttrValueMap.at("id").asString();
|
||||
}
|
||||
|
||||
return make_pair(attrValueMap, nullptr);
|
||||
});
|
||||
|
||||
|
@ -589,8 +620,33 @@ MyXMLVisitor::MyXMLVisitor(RichText* richText) : _fontElements(20), _richText(ri
|
|||
return make_pair(ValueMap(), richElement);
|
||||
});
|
||||
|
||||
MyXMLVisitor::setTagDescription("p", false, nullptr,
|
||||
[] { return RichElementNewLine::create(0, 2, Color3B::WHITE, 255); });
|
||||
MyXMLVisitor::setTagDescription(
|
||||
"p", true,
|
||||
[](const ValueMap& tagAttrValueMap) -> std::pair<ValueMap, RichElement*> {
|
||||
ValueMap attrValueMap;
|
||||
if (tagAttrValueMap.find("size") != tagAttrValueMap.end())
|
||||
{
|
||||
attrValueMap[RichText::KEY_FONT_SIZE] = tagAttrValueMap.at("size").asString();
|
||||
}
|
||||
|
||||
if (tagAttrValueMap.find("color") != tagAttrValueMap.end())
|
||||
{
|
||||
attrValueMap[RichText::KEY_FONT_COLOR_STRING] = tagAttrValueMap.at("color").asString();
|
||||
}
|
||||
|
||||
if (tagAttrValueMap.find("face") != tagAttrValueMap.end())
|
||||
{
|
||||
attrValueMap[RichText::KEY_FONT_FACE] = tagAttrValueMap.at("face").asString();
|
||||
}
|
||||
|
||||
if (tagAttrValueMap.find("id") != tagAttrValueMap.end())
|
||||
{
|
||||
attrValueMap[RichText::KEY_ID] = tagAttrValueMap.at("id").asString();
|
||||
}
|
||||
|
||||
return make_pair(attrValueMap, nullptr);
|
||||
},
|
||||
[] { return RichElementNewLine::create(0, 2, Color3B::WHITE, 255); });
|
||||
|
||||
constexpr auto headerTagEnterHandler = [](const ValueMap& tagAttrValueMap,
|
||||
float defaultFontSize) -> std::pair<ValueMap, RichElement*> {
|
||||
|
@ -616,6 +672,11 @@ MyXMLVisitor::MyXMLVisitor(RichText* richText) : _fontElements(20), _richText(ri
|
|||
|
||||
attrValueMap[RichText::KEY_TEXT_BOLD] = true;
|
||||
|
||||
if (tagAttrValueMap.find("id") != tagAttrValueMap.end())
|
||||
{
|
||||
attrValueMap[RichText::KEY_ID] = tagAttrValueMap.at("id").asString();
|
||||
}
|
||||
|
||||
return make_pair(attrValueMap, nullptr);
|
||||
};
|
||||
|
||||
|
@ -817,6 +878,16 @@ std::tuple<bool, Color3B> MyXMLVisitor::getGlow() const
|
|||
return std::make_tuple(false, Color3B::WHITE);
|
||||
}
|
||||
|
||||
std::string MyXMLVisitor::getName() const
|
||||
{
|
||||
for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
|
||||
{
|
||||
if (!i->name.empty())
|
||||
return i->name;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void MyXMLVisitor::startElement(void* /*ctx*/, const char* elementName, const char** atts)
|
||||
{
|
||||
auto it = _tagTables.find(elementName);
|
||||
|
@ -960,7 +1031,49 @@ void MyXMLVisitor::startElement(void* /*ctx*/, const char* elementName, const ch
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attrValueMap.find(RichText::KEY_URL) != attrValueMap.end())
|
||||
{
|
||||
attributes.url = attrValueMap.at(RichText::KEY_URL).asString();
|
||||
attributes.setColor(_richText->getAnchorFontColor3B());
|
||||
if (_richText->isAnchorTextBoldEnabled())
|
||||
{
|
||||
attributes.bold = true;
|
||||
}
|
||||
if (_richText->isAnchorTextItalicEnabled())
|
||||
{
|
||||
attributes.italics = true;
|
||||
}
|
||||
if (_richText->isAnchorTextUnderlineEnabled())
|
||||
{
|
||||
attributes.line = StyleLine::UNDERLINE;
|
||||
}
|
||||
if (_richText->isAnchorTextDelEnabled())
|
||||
{
|
||||
attributes.line = StyleLine::STRIKETHROUGH;
|
||||
}
|
||||
if (_richText->isAnchorTextOutlineEnabled())
|
||||
{
|
||||
attributes.effect = StyleEffect::OUTLINE;
|
||||
attributes.outlineColor = _richText->getAnchorTextOutlineColor3B();
|
||||
attributes.outlineSize = _richText->getAnchorTextOutlineSize();
|
||||
}
|
||||
if (_richText->isAnchorTextShadowEnabled())
|
||||
{
|
||||
attributes.effect = StyleEffect::SHADOW;
|
||||
attributes.shadowColor = _richText->getAnchorTextShadowColor3B();
|
||||
attributes.shadowOffset = _richText->getAnchorTextShadowOffset();
|
||||
attributes.shadowBlurRadius = _richText->getAnchorTextShadowBlurRadius();
|
||||
}
|
||||
if (_richText->isAnchorTextGlowEnabled())
|
||||
{
|
||||
attributes.effect = StyleEffect::GLOW;
|
||||
attributes.glowColor = _richText->getAnchorTextGlowColor3B();
|
||||
}
|
||||
}
|
||||
if (attrValueMap.find(RichText::KEY_ID) != attrValueMap.end())
|
||||
{
|
||||
attributes.name = attrValueMap.at(RichText::KEY_ID).asString();
|
||||
}
|
||||
pushBackFontElement(attributes);
|
||||
}
|
||||
if (richElement)
|
||||
|
@ -1017,6 +1130,7 @@ void MyXMLVisitor::textHandler(void* /*ctx*/, const char* str, size_t len)
|
|||
auto outline = getOutline();
|
||||
auto shadow = getShadow();
|
||||
auto glow = getGlow();
|
||||
auto name = getName();
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (italics)
|
||||
|
@ -1038,7 +1152,7 @@ void MyXMLVisitor::textHandler(void* /*ctx*/, const char* str, size_t len)
|
|||
|
||||
auto element = RichElementText::create(0, color, 255, text, face, fontSize, flags, url, std::get<1>(outline),
|
||||
std::get<2>(outline), std::get<1>(shadow), std::get<2>(shadow),
|
||||
std::get<3>(shadow), std::get<1>(glow));
|
||||
std::get<3>(shadow), std::get<1>(glow), name);
|
||||
_richText->pushBackElement(element);
|
||||
}
|
||||
|
||||
|
@ -1125,6 +1239,7 @@ const std::string RichText::KEY_ANCHOR_TEXT_SHADOW_OFFSET_WIDTH("KEY_ANCHOR_TEXT
|
|||
const std::string RichText::KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT("KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT");
|
||||
const std::string RichText::KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS("KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS");
|
||||
const std::string RichText::KEY_ANCHOR_TEXT_GLOW_COLOR("KEY_ANCHOR_TEXT_GLOW_COLOR");
|
||||
const std::string RichText::KEY_ID("KEY_ID");
|
||||
|
||||
RichText::RichText() : _formatTextDirty(true), _leftSpaceWidth(0.0f)
|
||||
{
|
||||
|
@ -1702,6 +1817,9 @@ void RichText::formatText(bool force)
|
|||
label->enableGlow(Color4B(elmtText->_glowColor));
|
||||
}
|
||||
label->setTextColor(Color4B(elmtText->_color));
|
||||
|
||||
label->setName(elmtText->_id);
|
||||
|
||||
elementRenderer = label;
|
||||
break;
|
||||
}
|
||||
|
@ -1732,6 +1850,7 @@ void RichText::formatText(bool force)
|
|||
UrlTouchListenerComponent::create(elementRenderer, elmtImage->_url,
|
||||
std::bind(&RichText::openUrl, this, std::placeholders::_1)));
|
||||
elementRenderer->setColor(element->_color);
|
||||
elementRenderer->setName(elmtImage->_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1774,7 +1893,7 @@ void RichText::formatText(bool force)
|
|||
handleTextRenderer(elmtText->_text, elmtText->_fontName, elmtText->_fontSize, elmtText->_color,
|
||||
elmtText->_opacity, elmtText->_flags, elmtText->_url, elmtText->_outlineColor,
|
||||
elmtText->_outlineSize, elmtText->_shadowColor, elmtText->_shadowOffset,
|
||||
elmtText->_shadowBlurRadius, elmtText->_glowColor);
|
||||
elmtText->_shadowBlurRadius, elmtText->_glowColor, elmtText->_id);
|
||||
break;
|
||||
}
|
||||
case RichElement::Type::IMAGE:
|
||||
|
@ -1782,13 +1901,13 @@ void RichText::formatText(bool force)
|
|||
RichElementImage* elmtImage = static_cast<RichElementImage*>(element);
|
||||
handleImageRenderer(elmtImage->_filePath, elmtImage->_textureType, elmtImage->_color,
|
||||
elmtImage->_opacity, elmtImage->_width, elmtImage->_height, elmtImage->_url,
|
||||
elmtImage->_scaleX, elmtImage->_scaleY);
|
||||
elmtImage->_scaleX, elmtImage->_scaleY, elmtImage->_id);
|
||||
break;
|
||||
}
|
||||
case RichElement::Type::CUSTOM:
|
||||
{
|
||||
RichElementCustomNode* elmtCustom = static_cast<RichElementCustomNode*>(element);
|
||||
handleCustomRenderer(elmtCustom->_customNode);
|
||||
handleCustomRenderer(elmtCustom->_customNode, elmtCustom->_id);
|
||||
break;
|
||||
}
|
||||
case RichElement::Type::NEWLINE:
|
||||
|
@ -1972,7 +2091,8 @@ void RichText::handleTextRenderer(std::string_view text,
|
|||
const Color3B& shadowColor,
|
||||
const Vec2& shadowOffset,
|
||||
int shadowBlurRadius,
|
||||
const Color3B& glowColor)
|
||||
const Color3B& glowColor,
|
||||
std::string_view id)
|
||||
{
|
||||
bool fileExist = FileUtils::getInstance()->isFileExist(fontName);
|
||||
RichText::WrapMode wrapMode = static_cast<RichText::WrapMode>(_defaults.at(KEY_WRAP_MODE).asInt());
|
||||
|
@ -1982,6 +2102,7 @@ void RichText::handleTextRenderer(std::string_view text,
|
|||
ss << text;
|
||||
std::string currentText;
|
||||
size_t realLines = 0;
|
||||
auto isFirstLabel = true;
|
||||
while (std::getline(ss, currentText, '\n'))
|
||||
{
|
||||
if (realLines > 0)
|
||||
|
@ -2026,6 +2147,12 @@ void RichText::handleTextRenderer(std::string_view text,
|
|||
textRenderer->setTextColor(Color4B(color));
|
||||
textRenderer->setOpacity(opacity);
|
||||
|
||||
if (isFirstLabel && !id.empty())
|
||||
{
|
||||
textRenderer->setName(id);
|
||||
isFirstLabel = false;
|
||||
}
|
||||
|
||||
// textRendererWidth will get 0.0f, when we've got glError: 0x0501 in Label::getContentSize
|
||||
// It happens when currentText is very very long so that can't generate a texture
|
||||
const float textRendererWidth = textRenderer->getContentSize().width;
|
||||
|
@ -2094,7 +2221,8 @@ void RichText::handleImageRenderer(std::string_view filePath,
|
|||
int height,
|
||||
std::string_view url,
|
||||
float scaleX,
|
||||
float scaleY)
|
||||
float scaleY,
|
||||
std::string_view id)
|
||||
{
|
||||
Sprite* imageRenderer;
|
||||
if (textureType == Widget::TextureResType::LOCAL)
|
||||
|
@ -2110,6 +2238,8 @@ void RichText::handleImageRenderer(std::string_view filePath,
|
|||
if (height != -1)
|
||||
imageRenderer->setScaleY(height / currentSize.height);
|
||||
|
||||
imageRenderer->setName(id);
|
||||
|
||||
imageRenderer->setScaleX(imageRenderer->getScaleX() * scaleX);
|
||||
imageRenderer->setScaleY(imageRenderer->getScaleY() * scaleY);
|
||||
|
||||
|
@ -2122,9 +2252,15 @@ void RichText::handleImageRenderer(std::string_view filePath,
|
|||
}
|
||||
}
|
||||
|
||||
void RichText::handleCustomRenderer(ax::Node* renderer)
|
||||
void RichText::handleCustomRenderer(ax::Node* renderer, std::string_view id)
|
||||
{
|
||||
Vec2 imgSize = renderer->getContentSize();
|
||||
|
||||
if (!id.empty())
|
||||
{
|
||||
renderer->setName(id);
|
||||
}
|
||||
|
||||
_leftSpaceWidth -= imgSize.width;
|
||||
if (_leftSpaceWidth < 0.0f)
|
||||
{
|
||||
|
|
|
@ -121,6 +121,7 @@ public:
|
|||
* @param shadowOffset shadow effect offset value
|
||||
* @param shadowBlurRadius the shadow effect blur radius
|
||||
* @param glowColor glow color
|
||||
* @param id ID of element
|
||||
* @return True if initialize success, false otherwise.
|
||||
*/
|
||||
bool init(int tag,
|
||||
|
@ -136,7 +137,8 @@ public:
|
|||
const Color3B& shadowColor = Color3B::BLACK,
|
||||
const Vec2& shadowOffset = Vec2(2.0, -2.0),
|
||||
int shadowBlurRadius = 0,
|
||||
const Color3B& glowColor = Color3B::WHITE);
|
||||
const Color3B& glowColor = Color3B::WHITE,
|
||||
std::string_view id = ""sv);
|
||||
|
||||
/**
|
||||
* @brief Create a RichElementText with various arguments.
|
||||
|
@ -155,6 +157,7 @@ public:
|
|||
* @param shadowOffset shadow effect offset value
|
||||
* @param shadowBlurRadius the shadow effect blur radius
|
||||
* @param glowColor glow color
|
||||
* @param id ID of element
|
||||
* @return RichElementText instance.
|
||||
*/
|
||||
static RichElementText* create(int tag,
|
||||
|
@ -170,7 +173,8 @@ public:
|
|||
const Color3B& shadowColor = Color3B::BLACK,
|
||||
const Vec2& shadowOffset = Vec2(2.0, -2.0),
|
||||
int shadowBlurRadius = 0,
|
||||
const Color3B& glowColor = Color3B::WHITE);
|
||||
const Color3B& glowColor = Color3B::WHITE,
|
||||
std::string_view id = ""sv);
|
||||
|
||||
protected:
|
||||
std::string _text;
|
||||
|
@ -184,6 +188,7 @@ protected:
|
|||
Vec2 _shadowOffset; /*!< shadow effect offset value */
|
||||
int _shadowBlurRadius; /*!< the shadow effect blur radius */
|
||||
Color3B _glowColor; /*!< attributes of glow tag */
|
||||
std::string _id; /*!< ID of this text field */
|
||||
friend class RichText;
|
||||
};
|
||||
|
||||
|
@ -210,6 +215,7 @@ public:
|
|||
* @param filePath A image file name.
|
||||
* @param url uniform resource locator
|
||||
* @param texType texture type, may be a valid file path, or a sprite frame name
|
||||
* @param id ID of element
|
||||
* @return True if initialize success, false otherwise.
|
||||
*/
|
||||
bool init(int tag,
|
||||
|
@ -217,7 +223,8 @@ public:
|
|||
uint8_t opacity,
|
||||
std::string_view filePath,
|
||||
std::string_view url = "",
|
||||
Widget::TextureResType texType = Widget::TextureResType::LOCAL);
|
||||
Widget::TextureResType texType = Widget::TextureResType::LOCAL,
|
||||
std::string_view id = ""sv);
|
||||
|
||||
/**
|
||||
* @brief Create a RichElementImage with various arguments.
|
||||
|
@ -228,6 +235,7 @@ public:
|
|||
* @param filePath A image file name.
|
||||
* @param url uniform resource locator
|
||||
* @param texType texture type, may be a valid file path, or a sprite frame name
|
||||
* @param id ID of element
|
||||
* @return A RichElementImage instance.
|
||||
*/
|
||||
static RichElementImage* create(int tag,
|
||||
|
@ -235,7 +243,8 @@ public:
|
|||
uint8_t opacity,
|
||||
std::string_view filePath,
|
||||
std::string_view url = "",
|
||||
Widget::TextureResType texType = Widget::TextureResType::LOCAL);
|
||||
Widget::TextureResType texType = Widget::TextureResType::LOCAL,
|
||||
std::string_view id = ""sv);
|
||||
|
||||
void setWidth(int width);
|
||||
void setHeight(int height);
|
||||
|
@ -243,6 +252,7 @@ public:
|
|||
void setScaleX(float scaleX) { _scaleX = scaleX; }
|
||||
void setScaleY(float scaleY) { _scaleY = scaleY; }
|
||||
void setUrl(std::string_view url);
|
||||
void setId(std::string_view id);
|
||||
|
||||
protected:
|
||||
std::string _filePath;
|
||||
|
@ -254,6 +264,7 @@ protected:
|
|||
float _scaleX;
|
||||
float _scaleY;
|
||||
std::string _url; /*!< attributes of anchor tag */
|
||||
std::string _id; /*!< attributes of anchor tag */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -283,9 +294,10 @@ public:
|
|||
* @param color A color in Color3B.
|
||||
* @param opacity A opacity in GLubyte.
|
||||
* @param customNode A custom node pointer.
|
||||
* @param id ID of element
|
||||
* @return True if initialize success, false otherwise.
|
||||
*/
|
||||
bool init(int tag, const Color3B& color, uint8_t opacity, Node* customNode);
|
||||
bool init(int tag, const Color3B& color, uint8_t opacity, Node* customNode, std::string_view id = ""sv);
|
||||
|
||||
/**
|
||||
* @brief Create a RichElementCustomNode with various arguments.
|
||||
|
@ -294,12 +306,19 @@ public:
|
|||
* @param color A color in Color3B.
|
||||
* @param opacity A opacity in GLubyte.
|
||||
* @param customNode A custom node pointer.
|
||||
* @param id ID of element
|
||||
* @return A RichElementCustomNode instance.
|
||||
*/
|
||||
static RichElementCustomNode* create(int tag, const Color3B& color, uint8_t opacity, Node* customNode);
|
||||
static RichElementCustomNode* create(int tag,
|
||||
const Color3B& color,
|
||||
uint8_t opacity,
|
||||
Node* customNode,
|
||||
std::string_view id = ""sv);
|
||||
|
||||
protected:
|
||||
Node* _customNode{};
|
||||
std::string _id;
|
||||
|
||||
friend class RichText;
|
||||
};
|
||||
|
||||
|
@ -394,7 +413,7 @@ public:
|
|||
static const std::string KEY_VERTICAL_SPACE; /*!< key of vertical space */
|
||||
static const std::string KEY_WRAP_MODE; /*!< key of per word, or per char */
|
||||
static const std::string KEY_HORIZONTAL_ALIGNMENT; /*!< key of left, right, or center */
|
||||
static const std::string KEY_VERTICAL_ALIGNMENT; /*!< key of left, right, or center */
|
||||
static const std::string KEY_VERTICAL_ALIGNMENT; /*!< key of left, right, or center */
|
||||
static const std::string KEY_FONT_COLOR_STRING; /*!< key of font color */
|
||||
static const std::string KEY_FONT_SIZE; /*!< key of font size */
|
||||
static const std::string KEY_FONT_SMALL; /*!< key of font size small */
|
||||
|
@ -431,6 +450,7 @@ public:
|
|||
static const std::string KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT; /*!< key of shadow offset (height) of anchor tag */
|
||||
static const std::string KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS; /*!< key of shadow blur radius of anchor tag */
|
||||
static const std::string KEY_ANCHOR_TEXT_GLOW_COLOR; /*!< key of glow color of anchor tag */
|
||||
static const std::string KEY_ID; /*!< key of id */
|
||||
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
|
@ -611,7 +631,8 @@ protected:
|
|||
const Color3B& shadowColor = Color3B::BLACK,
|
||||
const Vec2& shadowOffset = Vec2(2.0, -2.0),
|
||||
int shadowBlurRadius = 0,
|
||||
const Color3B& glowColor = Color3B::WHITE);
|
||||
const Color3B& glowColor = Color3B::WHITE,
|
||||
std::string_view id = ""sv);
|
||||
void handleImageRenderer(std::string_view filePath,
|
||||
Widget::TextureResType textureType,
|
||||
const Color3B& color,
|
||||
|
@ -619,9 +640,10 @@ protected:
|
|||
int width,
|
||||
int height,
|
||||
std::string_view url,
|
||||
float scaleX = 1.f,
|
||||
float scaleY = 1.f);
|
||||
void handleCustomRenderer(Node* renderer);
|
||||
float scaleX = 1.f,
|
||||
float scaleY = 1.f,
|
||||
std::string_view id = ""sv);
|
||||
void handleCustomRenderer(Node* renderer, std::string_view id = ""sv);
|
||||
void formatRenderers();
|
||||
void addNewLine(int quantity = 1);
|
||||
void doHorizontalAlignment(const Vector<Node*>& row, float rowWidth);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/****************************************************************************
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md).
|
||||
|
||||
https://axmolengine.github.io/
|
||||
|
||||
|
@ -32,19 +33,23 @@ THE SOFTWARE.
|
|||
#include "2d/Camera.h"
|
||||
NS_AX_BEGIN
|
||||
|
||||
static const int NUMBER_OF_GATHERED_TOUCHES_FOR_MOVE_SPEED = 5;
|
||||
static const float OUT_OF_BOUNDARY_BREAKING_FACTOR = 0.05f;
|
||||
static const float BOUNCE_BACK_DURATION = 1.0f;
|
||||
namespace
|
||||
{
|
||||
constexpr float DEFAULT_TIME_IN_SEC_FOR_SCROLL_TO_ITEM = 1.0f;
|
||||
constexpr int NUMBER_OF_GATHERED_TOUCHES_FOR_MOVE_SPEED = 5;
|
||||
constexpr float OUT_OF_BOUNDARY_BREAKING_FACTOR = 0.05f;
|
||||
constexpr float BOUNCE_BACK_DURATION = 1.0f;
|
||||
|
||||
#define MOVE_INCH 7.0f / 160.0f
|
||||
|
||||
static float convertDistanceFromPointToInch(const Vec2& dis)
|
||||
float convertDistanceFromPointToInch(const Vec2& dis)
|
||||
{
|
||||
auto glView = Director::getInstance()->getGLView();
|
||||
int dpi = Device::getDPI();
|
||||
float distance = Vec2(dis.x * glView->getScaleX() / dpi, dis.y * glView->getScaleY() / dpi).getLength();
|
||||
return distance;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#define MOVE_INCH (7.0f / 160.0f)
|
||||
|
||||
namespace ui
|
||||
{
|
||||
|
@ -78,6 +83,7 @@ ScrollView::ScrollView()
|
|||
, _horizontalScrollBar(nullptr)
|
||||
, _scrollViewEventListener(nullptr)
|
||||
, _eventCallback(nullptr)
|
||||
, _scrollTime(DEFAULT_TIME_IN_SEC_FOR_SCROLL_TO_ITEM)
|
||||
{
|
||||
setTouchEnabled(true);
|
||||
_propagateTouchEvents = false;
|
||||
|
@ -1587,6 +1593,51 @@ Widget* ScrollView::findNextFocusedWidget(ax::ui::Widget::FocusDirection directi
|
|||
return Widget::findNextFocusedWidget(direction, current);
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollView::setScrollDuration(float time)
|
||||
{
|
||||
if (time >= 0)
|
||||
_scrollTime = time;
|
||||
}
|
||||
|
||||
float ScrollView::getScrollDuration() const
|
||||
{
|
||||
return _scrollTime;
|
||||
}
|
||||
|
||||
Vec2 ScrollView::calculateItemPositionWithAnchor(const Node* node, const Vec2& itemAnchorPoint)
|
||||
{
|
||||
auto boundingBox = node->getBoundingBox();
|
||||
Vec2 origin(boundingBox.origin.x, boundingBox.origin.y);
|
||||
Vec2 size = node->getContentSize();
|
||||
return origin + Vec2(size.width * itemAnchorPoint.x, size.height * itemAnchorPoint.y);
|
||||
}
|
||||
|
||||
Vec2 ScrollView::calculateItemDestination(const Vec2& positionRatioInView, const Node* item, const Vec2& itemAnchorPoint)
|
||||
{
|
||||
const Vec2& contentSize = getContentSize();
|
||||
Vec2 positionInView;
|
||||
positionInView.x += contentSize.width * positionRatioInView.x;
|
||||
positionInView.y += contentSize.height * positionRatioInView.y;
|
||||
|
||||
Vec2 itemPosition = calculateItemPositionWithAnchor(item, itemAnchorPoint);
|
||||
return -(itemPosition - positionInView);
|
||||
}
|
||||
|
||||
void ScrollView::scrollToItem(Node* item, const Vec2& positionRatioInView, const Vec2& itemAnchorPoint)
|
||||
{
|
||||
scrollToItem(item, positionRatioInView, itemAnchorPoint, _scrollTime);
|
||||
}
|
||||
|
||||
void ScrollView::scrollToItem(Node* item, const Vec2& positionRatioInView, const Vec2& itemAnchorPoint, float timeInSec)
|
||||
{
|
||||
if (item == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto destination = calculateItemDestination(positionRatioInView, item, itemAnchorPoint);
|
||||
startAutoScrollToDestination(destination, timeInSec, true);
|
||||
}
|
||||
} // namespace ui
|
||||
|
||||
NS_AX_END
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/****************************************************************************
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md).
|
||||
|
||||
https://axmolengine.github.io/
|
||||
|
||||
|
@ -101,7 +102,7 @@ public:
|
|||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
virtual ~ScrollView();
|
||||
~ScrollView() override;
|
||||
|
||||
/**
|
||||
* Create an empty ScrollView.
|
||||
|
@ -340,24 +341,24 @@ public:
|
|||
virtual void addEventListener(const ccScrollViewCallback& callback);
|
||||
|
||||
// override functions
|
||||
virtual void addChild(Node* child) override;
|
||||
virtual void addChild(Node* child, int localZOrder) override;
|
||||
virtual void addChild(Node* child, int localZOrder, int tag) override;
|
||||
virtual void addChild(Node* child, int localZOrder, std::string_view name) override;
|
||||
virtual void removeAllChildren() override;
|
||||
virtual void removeAllChildrenWithCleanup(bool cleanup) override;
|
||||
virtual void removeChild(Node* child, bool cleanup = true) override;
|
||||
virtual Vector<Node*>& getChildren() override;
|
||||
virtual const Vector<Node*>& getChildren() const override;
|
||||
virtual ssize_t getChildrenCount() const override;
|
||||
virtual Node* getChildByTag(int tag) const override;
|
||||
virtual Node* getChildByName(std::string_view name) const override;
|
||||
void addChild(Node* child) override;
|
||||
void addChild(Node* child, int localZOrder) override;
|
||||
void addChild(Node* child, int localZOrder, int tag) override;
|
||||
void addChild(Node* child, int localZOrder, std::string_view name) override;
|
||||
void removeAllChildren() override;
|
||||
void removeAllChildrenWithCleanup(bool cleanup) override;
|
||||
void removeChild(Node* child, bool cleanup = true) override;
|
||||
Vector<Node*>& getChildren() override;
|
||||
const Vector<Node*>& getChildren() const override;
|
||||
ssize_t getChildrenCount() const override;
|
||||
Node* getChildByTag(int tag) const override;
|
||||
Node* getChildByName(std::string_view name) const override;
|
||||
// touch event callback
|
||||
virtual bool onTouchBegan(Touch* touch, Event* unusedEvent) override;
|
||||
virtual void onTouchMoved(Touch* touch, Event* unusedEvent) override;
|
||||
virtual void onTouchEnded(Touch* touch, Event* unusedEvent) override;
|
||||
virtual void onTouchCancelled(Touch* touch, Event* unusedEvent) override;
|
||||
virtual void update(float dt) override;
|
||||
bool onTouchBegan(Touch* touch, Event* unusedEvent) override;
|
||||
void onTouchMoved(Touch* touch, Event* unusedEvent) override;
|
||||
void onTouchEnded(Touch* touch, Event* unusedEvent) override;
|
||||
void onTouchCancelled(Touch* touch, Event* unusedEvent) override;
|
||||
void update(float dt) override;
|
||||
|
||||
/**
|
||||
* @brief Toggle bounce enabled when scroll to the edge.
|
||||
|
@ -453,7 +454,7 @@ public:
|
|||
/**
|
||||
* @brief Set the scroll bar's color
|
||||
*
|
||||
* @param the scroll bar's color
|
||||
* @param color the scroll bar's color
|
||||
*/
|
||||
void setScrollBarColor(const Color3B& color);
|
||||
|
||||
|
@ -467,7 +468,7 @@ public:
|
|||
/**
|
||||
* @brief Set the scroll bar's opacity
|
||||
*
|
||||
* @param the scroll bar's opacity
|
||||
* @param opacity the scroll bar's opacity
|
||||
*/
|
||||
void setScrollBarOpacity(uint8_t opacity);
|
||||
|
||||
|
@ -481,7 +482,7 @@ public:
|
|||
/**
|
||||
* @brief Set scroll bar auto hide state
|
||||
*
|
||||
* @param scroll bar auto hide state
|
||||
* @param autoHideEnabled scroll bar auto hide state
|
||||
*/
|
||||
void setScrollBarAutoHideEnabled(bool autoHideEnabled);
|
||||
|
||||
|
@ -495,7 +496,7 @@ public:
|
|||
/**
|
||||
* @brief Set scroll bar auto hide time
|
||||
*
|
||||
* @param scroll bar auto hide time
|
||||
* @param autoHideTime bar auto hide time
|
||||
*/
|
||||
void setScrollBarAutoHideTime(float autoHideTime);
|
||||
|
||||
|
@ -509,7 +510,7 @@ public:
|
|||
/**
|
||||
* @brief Set the touch total time threshold
|
||||
*
|
||||
* @param the touch total time threshold
|
||||
* @param touchTotalTimeThreshold the touch total time threshold
|
||||
*/
|
||||
void setTouchTotalTimeThreshold(float touchTotalTimeThreshold);
|
||||
|
||||
|
@ -526,7 +527,7 @@ public:
|
|||
* @see `Layout::Type`
|
||||
* @param type Layout type enum.
|
||||
*/
|
||||
virtual void setLayoutType(Type type) override;
|
||||
void setLayoutType(Type type) override;
|
||||
|
||||
/**
|
||||
* Get the layout type for scrollview.
|
||||
|
@ -534,22 +535,22 @@ public:
|
|||
* @see `Layout::Type`
|
||||
* @return LayoutType
|
||||
*/
|
||||
virtual Type getLayoutType() const override;
|
||||
Type getLayoutType() const override;
|
||||
|
||||
/**
|
||||
* Return the "class name" of widget.
|
||||
*/
|
||||
virtual std::string getDescription() const override;
|
||||
std::string getDescription() const override;
|
||||
|
||||
/**
|
||||
* @lua NA
|
||||
*/
|
||||
virtual void onEnter() override;
|
||||
void onEnter() override;
|
||||
|
||||
/**
|
||||
* @lua NA
|
||||
*/
|
||||
virtual void onExit() override;
|
||||
void onExit() override;
|
||||
|
||||
/**
|
||||
* When a widget is in a layout, you could call this method to get the next focused widget within a specified
|
||||
|
@ -558,7 +559,22 @@ public:
|
|||
*@param current the current focused widget
|
||||
*@return the next focused widget in a layout
|
||||
*/
|
||||
virtual Widget* findNextFocusedWidget(FocusDirection direction, Widget* current) override;
|
||||
Widget* findNextFocusedWidget(FocusDirection direction, Widget* current) override;
|
||||
|
||||
/**
|
||||
* Set the time in seconds to scroll between items.
|
||||
* Subsequent calls of function 'scrollToItem', will take 'time' seconds for scrolling.
|
||||
* @param time The seconds needed to scroll between two items. 'time' must be >= 0
|
||||
* @see scrollToItem(ssize_t, const Vec2&, const Vec2&)
|
||||
*/
|
||||
void setScrollDuration(float time);
|
||||
|
||||
/**
|
||||
* Get the time in seconds to scroll between items.
|
||||
* @return The time in seconds to scroll between items
|
||||
* @see setScrollDuration(float)
|
||||
*/
|
||||
float getScrollDuration() const;
|
||||
|
||||
/**
|
||||
* @return Whether the user is currently dragging the ScrollView to scroll it
|
||||
|
@ -569,7 +585,26 @@ public:
|
|||
*/
|
||||
bool isAutoScrolling() const { return _autoScrolling; }
|
||||
|
||||
virtual bool init() override;
|
||||
bool init() override;
|
||||
|
||||
/**
|
||||
* @brief Scroll to specific item
|
||||
* @param item Item to scroll to
|
||||
* @param positionRatioInView Specifies the position with ratio in list view's content size.
|
||||
* @param itemAnchorPoint Specifies an anchor point of each item for position to calculate distance.
|
||||
*/
|
||||
void scrollToItem(Node* item, const Vec2& positionRatioInView, const Vec2& itemAnchorPoint);
|
||||
|
||||
/**
|
||||
* @brief Scroll to specific item
|
||||
* @param item Item to scroll to
|
||||
* @param positionRatioInView Specifies the position with ratio in list view's content size.
|
||||
* @param itemAnchorPoint Specifies an anchor point of each item for position to calculate distance.
|
||||
* @param timeInSec Scroll time
|
||||
*/
|
||||
void scrollToItem(Node* item, const Vec2& positionRatioInView, const Vec2& itemAnchorPoint, float timeInSec);
|
||||
|
||||
static Vec2 calculateItemPositionWithAnchor(const Node* node, const Vec2& itemAnchorPoint);
|
||||
|
||||
protected:
|
||||
enum class MoveDirection
|
||||
|
@ -580,14 +615,14 @@ protected:
|
|||
RIGHT,
|
||||
};
|
||||
|
||||
virtual void initRenderer() override;
|
||||
void initRenderer() override;
|
||||
|
||||
virtual void onSizeChanged() override;
|
||||
virtual void doLayout() override;
|
||||
void onSizeChanged() override;
|
||||
void doLayout() override;
|
||||
|
||||
virtual Widget* createCloneInstance() override;
|
||||
virtual void copySpecialProperties(Widget* model) override;
|
||||
virtual void copyClonedWidgetChildren(Widget* model) override;
|
||||
Widget* createCloneInstance() override;
|
||||
void copySpecialProperties(Widget* model) override;
|
||||
void copyClonedWidgetChildren(Widget* model) override;
|
||||
|
||||
virtual void initScrollBar();
|
||||
virtual void removeScrollBar();
|
||||
|
@ -622,7 +657,7 @@ protected:
|
|||
virtual void handleMoveLogic(Touch* touch);
|
||||
virtual void handleReleaseLogic(Touch* touch);
|
||||
|
||||
virtual void interceptTouchEvent(Widget::TouchEventType event, Widget* sender, Touch* touch) override;
|
||||
void interceptTouchEvent(Widget::TouchEventType event, Widget* sender, Touch* touch) override;
|
||||
|
||||
void processScrollEvent(MoveDirection dir, bool bounce);
|
||||
void processScrollingEvent();
|
||||
|
@ -631,6 +666,8 @@ protected:
|
|||
|
||||
void updateScrollBar(const Vec2& outOfBoundary);
|
||||
|
||||
Vec2 calculateItemDestination(const Vec2& positionRatioInView, const Node* item, const Vec2& itemAnchorPoint);
|
||||
|
||||
protected:
|
||||
virtual float getAutoScrollStopEpsilon() const;
|
||||
bool fltEqualZero(const Vec2& point) const;
|
||||
|
@ -678,6 +715,8 @@ protected:
|
|||
|
||||
Ref* _scrollViewEventListener;
|
||||
ccScrollViewCallback _eventCallback;
|
||||
|
||||
float _scrollTime;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
|
|
@ -53,6 +53,7 @@ UIRichTextTests::UIRichTextTests()
|
|||
ADD_TEST_CASE(UIRichTextNewline);
|
||||
ADD_TEST_CASE(UIRichTextHeaders);
|
||||
ADD_TEST_CASE(UIRichTextParagraph);
|
||||
ADD_TEST_CASE(UIRichTextScrollTo);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -1053,3 +1054,130 @@ bool UIRichTextParagraph::init()
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UIRichTextScrollTo::init()
|
||||
{
|
||||
if (UIRichTextTestBase::init())
|
||||
{
|
||||
auto& widgetSize = _widget->getContentSize();
|
||||
|
||||
// Add the alert
|
||||
Text* alert = Text::create("Paragraph Tag", "fonts/Marker Felt.ttf", 30);
|
||||
alert->setColor(Color3B(159, 168, 176));
|
||||
alert->setPosition(
|
||||
Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f - alert->getContentSize().height * 3.125));
|
||||
_widget->addChild(alert);
|
||||
|
||||
createButtonPanel();
|
||||
|
||||
#ifdef AX_PLATFORM_PC
|
||||
_defaultContentSize = Size(290, 150);
|
||||
#endif
|
||||
|
||||
// ScrollView
|
||||
_scrollView = ScrollView::create();
|
||||
_scrollView->setContentSize(_defaultContentSize);
|
||||
_scrollView->setInnerContainerSize(_defaultContentSize);
|
||||
_scrollView->setBounceEnabled(true);
|
||||
_scrollView->setDirection(ScrollView::Direction::VERTICAL);
|
||||
_scrollView->setLayoutType(Layout::Type::VERTICAL);
|
||||
_scrollView->setScrollBarEnabled(true);
|
||||
_scrollView->setScrollBarWidth(4);
|
||||
_scrollView->setScrollBarPositionFromCorner(Vec2(2, 2));
|
||||
_scrollView->setScrollBarColor(Color3B::WHITE);
|
||||
_scrollView->setScrollBarAutoHideEnabled(false);
|
||||
_scrollView->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
|
||||
_scrollView->setPosition(_widget->getContentSize() / 2);
|
||||
_scrollView->setLocalZOrder(10);
|
||||
_widget->addChild(_scrollView);
|
||||
|
||||
ValueMap valMap;
|
||||
valMap[RichText::KEY_ANCHOR_FONT_COLOR_STRING] = "#00ffdd";
|
||||
|
||||
// RichText
|
||||
_richText = RichText::createWithXML(
|
||||
R"(<h1>Quick Links</h1>
|
||||
<a href="#fancy_header">Jump To Fancy Header</a><br/>
|
||||
<a href="#paragraph_2">Jump To Second Paragraph</a><br/>
|
||||
<a href="#some_link">Jump To Web Search</a><br/>
|
||||
<br/>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
|
||||
dolore magna aliqua.
|
||||
Purus faucibus ornare suspendisse sed nisi. Viverra aliquet eget sit amet tellus cras adipiscing.
|
||||
Ut tellus elementum sagittis vitae.
|
||||
Risus feugiat in ante metus dictum at. Semper eget duis at tellus at. Iaculis eu non diam phasellus vestibulum
|
||||
lorem sed risus.
|
||||
Sed vulputate odio ut enim. Morbi tristique senectus et netus et malesuada fames.</p>
|
||||
<h1 id="fancy_header">Fancy Header</h1>
|
||||
<p id="paragraph_2">This is the second paragraph! Cras sed felis eget velit aliquet sagittis id consectetur purus.
|
||||
Turpis nunc eget lorem dolor sed viverra ipsum nunc.
|
||||
Ultrices tincidunt arcu non sodales neque sodales ut etiam sit. Risus feugiat in ante metus dictum at tempor.
|
||||
Id neque aliquam vestibulum morbi blandit cursus risus.</p>
|
||||
<p>Tortor condimentum lacinia quis vel eros donec ac. Molestie ac feugiat sed lectus.
|
||||
Aliquam id diam maecenas ultricies mi eget mauris.
|
||||
Ullamcorper malesuada proin libero nunc consequat interdum varius.
|
||||
Sollicitudin nibh sit amet commodo nulla facilisi nullam vehicula ipsum.
|
||||
Diam quam nulla porttitor massa id neque aliquam vestibulum morbi. Sed velit dignissim sodales ut.
|
||||
Morbi leo urna molestie at elementum eu facilisis.
|
||||
Cursus metus aliquam eleifend mi in. Euismod lacinia at quis risus sed vulputate odio.
|
||||
Sit amet mattis vulputate enim nulla aliquet porttitor lacus luctus.</p>
|
||||
<a id="some_link" href="https://google.com">Google!</a>)",
|
||||
valMap);
|
||||
_richText->ignoreContentAdaptWithSize(false);
|
||||
_richText->setContentSize(Size(_defaultContentSize.width, 0));
|
||||
_richText->setAnchorPoint(Vec2::ANCHOR_MIDDLE_TOP);
|
||||
_richText->setOpenUrlHandler([this](std::string_view url) {
|
||||
// Check if the href starts with a "#" character
|
||||
if (url.starts_with('#'))
|
||||
{
|
||||
auto* node = _richText->getProtectedChildByName(url.substr(1));
|
||||
if (node)
|
||||
{
|
||||
// Scroll to the location of that node, and the reason it works is because
|
||||
// the ScrollView inner container is the same height as the RichText
|
||||
_scrollView->scrollToItem(node, Vec2::ANCHOR_MIDDLE_TOP, Vec2::ANCHOR_MIDDLE_TOP);
|
||||
}
|
||||
}
|
||||
else if (!url.empty())
|
||||
{
|
||||
Application::getInstance()->openURL(url);
|
||||
}
|
||||
});
|
||||
|
||||
const auto contentSize = _scrollView->getInnerContainerSize();
|
||||
_richText->setPosition(Vec2(contentSize.width / 2, contentSize.height));
|
||||
_richText->setLocalZOrder(10);
|
||||
|
||||
_richText->formatText();
|
||||
_scrollView->addChild(_richText);
|
||||
|
||||
updateScrollViewSize();
|
||||
|
||||
// test remove all children, this call won't effect the test
|
||||
_richText->removeAllChildren();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UIRichTextScrollTo::updateScrollViewSize()
|
||||
{
|
||||
auto newHeight = 0.f;
|
||||
auto&& children = _scrollView->getChildren();
|
||||
for (auto&& child : children)
|
||||
{
|
||||
auto&& contentSize = child->getContentSize();
|
||||
newHeight += contentSize.height;
|
||||
if (const auto* widget = dynamic_cast<Widget*>(child))
|
||||
{
|
||||
if (const auto* layoutParam = widget->getLayoutParameter())
|
||||
{
|
||||
auto&& margin = layoutParam->getMargin();
|
||||
newHeight += margin.top + margin.bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
_scrollView->setInnerContainerSize(Size(_scrollView->getInnerContainerSize().width, newHeight));
|
||||
_scrollView->scrollToTop(0.f, false);
|
||||
}
|
||||
|
|
|
@ -220,4 +220,17 @@ public:
|
|||
bool init() override;
|
||||
};
|
||||
|
||||
class UIRichTextScrollTo : public UIRichTextTestBase
|
||||
{
|
||||
public:
|
||||
CREATE_FUNC(UIRichTextScrollTo);
|
||||
|
||||
bool init() override;
|
||||
|
||||
protected:
|
||||
void updateScrollViewSize();
|
||||
|
||||
ax::ui::ScrollView* _scrollView;
|
||||
};
|
||||
|
||||
#endif /* defined(__TestCpp__UIRichTextTest__) */
|
||||
|
|
Loading…
Reference in New Issue