diff --git a/cocos/2d/CCFontFreeType.cpp b/cocos/2d/CCFontFreeType.cpp index e8e0bb2daa..191d65c46e 100644 --- a/cocos/2d/CCFontFreeType.cpp +++ b/cocos/2d/CCFontFreeType.cpp @@ -141,7 +141,10 @@ bool FontFreeType::createFontObject(const std::string &fontName, float fontSize) if (FT_New_Memory_Face(getFTLibrary(), s_cacheFontData[fontName].data.getBytes(), s_cacheFontData[fontName].data.getSize(), 0, &face )) return false; - + + face->underline_position = 0; + face->underline_thickness = 3; + if (FT_Select_Charmap(face, FT_ENCODING_UNICODE)) { int foundIndex = -1; diff --git a/cocos/2d/CCLabel.cpp b/cocos/2d/CCLabel.cpp index 8220a75ec9..533fe174b8 100644 --- a/cocos/2d/CCLabel.cpp +++ b/cocos/2d/CCLabel.cpp @@ -24,6 +24,9 @@ ****************************************************************************/ #include "2d/CCLabel.h" + +#include + #include "2d/CCFont.h" #include "2d/CCFontAtlasCache.h" #include "2d/CCFontAtlas.h" @@ -43,6 +46,7 @@ NS_CC_BEGIN +static const int UNDERLINE_NODE_TAG = 0xaabbccdd; /** * LabelLetter used to update the quad in texture atlas without SpriteBatchNode. */ @@ -207,7 +211,6 @@ Label* Label::createWithSystemFont(const std::string& text, const std::string& f return ret; } - delete ret; return nullptr; } @@ -381,6 +384,9 @@ Label::Label(TextHAlignment hAlignment /* = TextHAlignment::LEFT */, , _fontAtlas(nullptr) , _reusedLetter(nullptr) , _horizontalKernings(nullptr) +, _boldEnabled(false) +, _underlineNode(nullptr) +, _strikethroughEnabled(false) { setAnchorPoint(Vec2::ANCHOR_MIDDLE); reset(); @@ -510,7 +516,14 @@ void Label::reset() _bmfontScale = 1.0f; _overflow = Overflow::NONE; _originalFontSize = 0.0f; - + _boldEnabled = false; + if (_underlineNode) + { + removeChild(_underlineNode); + _underlineNode = nullptr; + } + _strikethroughEnabled = false; + setRotationSkewX(0); // reverse italics } void Label::updateShaderProgram() @@ -956,6 +969,16 @@ bool Label::setTTFConfigInternal(const TTFConfig& ttfConfig) _currLabelEffect = LabelEffect::NORMAL; updateShaderProgram(); } + + if (_fontConfig.italics) + this->enableItalics(); + if (_fontConfig.bold) + this->enableBold(); + if (_fontConfig.underline) + this->enableUnderline(); + if (_fontConfig.strikethrough) + this->enableStrikethrough(); + return true; } @@ -1096,55 +1119,112 @@ void Label::enableShadow(const Color4B& shadowColor /* = Color4B::BLACK */,const } } +void Label::enableItalics() +{ + setRotationSkewX(12); +} + +void Label::enableBold() +{ + if (!_boldEnabled) + { + // bold is implemented with outline + enableShadow(Color4B::WHITE, Size(0.9,0), 0); + // add one to kerning + setAdditionalKerning(_additionalKerning+1); + _boldEnabled = true; + } +} + +void Label::enableUnderline() +{ + // remove it, just in case to prevent adding two or more + if (!_underlineNode) + { + _underlineNode = DrawNode::create(); + addChild(_underlineNode, 100000); + _contentDirty = true; + } +} + +void Label::enableStrikethrough() +{ + if (!_strikethroughEnabled) + { + enableUnderline(); + _strikethroughEnabled = true; + } +} + void Label::disableEffect() { - disableEffect(LabelEffect::GLOW); - disableEffect(LabelEffect::OUTLINE); - disableEffect(LabelEffect::SHADOW); + disableEffect(LabelEffect::ALL); } void Label::disableEffect(LabelEffect effect) { switch (effect) { - case cocos2d::LabelEffect::NORMAL: - break; - case cocos2d::LabelEffect::OUTLINE: - if (_currLabelEffect == LabelEffect::OUTLINE) - { - if (_currentLabelType == LabelType::TTF) + case cocos2d::LabelEffect::NORMAL: + break; + case cocos2d::LabelEffect::OUTLINE: + if (_currLabelEffect == LabelEffect::OUTLINE) { - _fontConfig.outlineSize = 0; - setTTFConfig(_fontConfig); + if (_currentLabelType == LabelType::TTF) + { + _fontConfig.outlineSize = 0; + setTTFConfig(_fontConfig); + } + _currLabelEffect = LabelEffect::NORMAL; + _contentDirty = true; } - - _currLabelEffect = LabelEffect::NORMAL; - _contentDirty = true; - } - break; - case cocos2d::LabelEffect::SHADOW: - if (_shadowEnabled) - { - _shadowEnabled = false; - CC_SAFE_RELEASE_NULL(_shadowNode); - } - break; - case cocos2d::LabelEffect::GLOW: - if (_currLabelEffect == LabelEffect::GLOW) - { - _currLabelEffect = LabelEffect::NORMAL; - updateShaderProgram(); - } - break; - case LabelEffect::ALL: + break; + case cocos2d::LabelEffect::SHADOW: + if (_shadowEnabled) + { + _shadowEnabled = false; + CC_SAFE_RELEASE_NULL(_shadowNode); + } + break; + case cocos2d::LabelEffect::GLOW: + if (_currLabelEffect == LabelEffect::GLOW) + { + _currLabelEffect = LabelEffect::NORMAL; + updateShaderProgram(); + } + break; + case cocos2d::LabelEffect::ITALICS: + setRotationSkewX(0); + break; + case cocos2d::LabelEffect::BOLD: + _boldEnabled = false; + _additionalKerning -= 1; + disableEffect(LabelEffect::SHADOW); + break; + case cocos2d::LabelEffect::UNDERLINE: + if (_underlineNode) { + removeChild(_underlineNode); + _underlineNode = nullptr; + } + break; + case cocos2d::LabelEffect::STRIKETHROUGH: + _strikethroughEnabled = false; + // since it is based on underline, disable it as well + disableEffect(LabelEffect::UNDERLINE); + break; + case LabelEffect::ALL: { disableEffect(LabelEffect::SHADOW); disableEffect(LabelEffect::GLOW); disableEffect(LabelEffect::OUTLINE); + disableEffect(LabelEffect::ITALICS); + disableEffect(LabelEffect::BOLD); + disableEffect(LabelEffect::UNDERLINE); + disableEffect(LabelEffect::STRIKETHROUGH); } - break; - default: - break; + break; + default: + break; } } @@ -1299,6 +1379,25 @@ void Label::updateContent() createShadowSpriteForSystemFont(fontDef); } } + + if (_underlineNode) + { + const float charheight = (_textDesiredHeight / _numberOfLines); + + _underlineNode->clear(); + _underlineNode->setLineWidth(charheight/6); + + for (int i=0; i<_numberOfLines; ++i) + { + float offsety = 0; + if (_strikethroughEnabled) + offsety += charheight / 2; + // FIXME: Might not work with different vertical alignments + float y = (_numberOfLines - i - 1) * charheight + offsety; + _underlineNode->drawLine(Vec2(_linesOffsetX[i],y), Vec2(_linesWidth[i] + _linesOffsetX[i],y), _textColorF); + } + } + if(updateFinished){ _contentDirty = false; } @@ -1327,16 +1426,16 @@ float Label::getBMFontSize()const return _bmFontSize; } -void Label::onDrawShadow(GLProgram* glProgram) +void Label::onDrawShadow(GLProgram* glProgram, const Color4F& shadowColor) { if (_currentLabelType == LabelType::TTF) { glProgram->setUniformLocationWith4f(_uniformTextColor, - _shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a); + shadowColor.r, shadowColor.g, shadowColor.b, shadowColor.a); if (_currLabelEffect == LabelEffect::OUTLINE || _currLabelEffect == LabelEffect::GLOW) { glProgram->setUniformLocationWith4f(_uniformEffectColor, - _shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a); + shadowColor.r, shadowColor.g, shadowColor.b, shadowColor.a); } glProgram->setUniformsForBuiltins(_shadowTransform); @@ -1353,8 +1452,8 @@ void Label::onDrawShadow(GLProgram* glProgram) { Color3B oldColor = _realColor; GLubyte oldOPacity = _displayedOpacity; - _displayedOpacity = _shadowOpacity; - setColor(_shadowColor3B); + _displayedOpacity = shadowColor.a * 255; + setColor(Color3B(shadowColor)); glProgram->setUniformsForBuiltins(_shadowTransform); for (auto&& it : _letters) @@ -1379,7 +1478,10 @@ void Label::onDraw(const Mat4& transform, bool transformUpdated) if (_shadowEnabled) { - onDrawShadow(glprogram); + if (_boldEnabled) + onDrawShadow(glprogram, _textColorF); + else + onDrawShadow(glprogram, _shadowColor4F); } glprogram->setUniformsForBuiltins(transform); @@ -1672,18 +1774,21 @@ float Label::getLineSpacing() const void Label::setAdditionalKerning(float space) { - CCASSERT(_currentLabelType != LabelType::STRING_TEXTURE, "Not supported system font!"); - if (_additionalKerning != space) + + if (_currentLabelType != LabelType::STRING_TEXTURE) { - _additionalKerning = space; - _contentDirty = true; + if (_additionalKerning != space) + { + _additionalKerning = space; + _contentDirty = true; + } } + else + CCLOG("Label::setAdditionalKerning not supported on LabelType::STRING_TEXTURE"); } float Label::getAdditionalKerning() const { - CCASSERT(_currentLabelType != LabelType::STRING_TEXTURE, "Not supported system font!"); - return _additionalKerning; } diff --git a/cocos/2d/CCLabel.h b/cocos/2d/CCLabel.h index 480e22761d..bce5bc270f 100644 --- a/cocos/2d/CCLabel.h +++ b/cocos/2d/CCLabel.h @@ -55,14 +55,24 @@ typedef struct _ttfConfig bool distanceFieldEnabled; int outlineSize; + bool italics; + bool bold; + bool underline; + bool strikethrough; + _ttfConfig(const std::string& filePath = "",float size = 12, const GlyphCollection& glyphCollection = GlyphCollection::DYNAMIC, - const char *customGlyphCollection = nullptr, bool useDistanceField = false, int outline = 0) + const char *customGlyphCollection = nullptr, bool useDistanceField = false, int outline = 0, + bool useItalics = false, bool useBold = false, bool useUnderline = false, bool useStrikethrough = false) : fontFilePath(filePath) , fontSize(size) , glyphs(glyphCollection) , customGlyphs(customGlyphCollection) , distanceFieldEnabled(useDistanceField) , outlineSize(outline) + , italics(useItalics) + , bold(useBold) + , underline(useUnderline) + , strikethrough(useStrikethrough) { if(outline > 0) { @@ -336,6 +346,27 @@ public: */ virtual void enableGlow(const Color4B& glowColor); + /** + * Enable italics rendering + */ + void enableItalics(); + + /** + * Enable bold rendering + */ + void enableBold(); + + /** + * Enable underline + */ + void enableUnderline(); + + /** + * Enables strikethrough. + * Underline and Strikethrough cannot be enabled at the same time. + * Strikethough is like an underline but at the middle of the glyph + */ + void enableStrikethrough(); /** * Disable all effect to Label. * @warning Please use disableEffect(LabelEffect::ALL) instead of this API. @@ -605,7 +636,7 @@ protected: void computeStringNumLines(); void onDraw(const Mat4& transform, bool transformUpdated); - void onDrawShadow(GLProgram* glProgram); + void onDrawShadow(GLProgram* glProgram, const Color4F& shadowColor); void drawSelf(bool visibleByCamera, Renderer* renderer, uint32_t flags); bool multilineTextWrapByChar(); @@ -735,6 +766,11 @@ protected: float _bmfontScale; Overflow _overflow; float _originalFontSize; + + bool _boldEnabled; + DrawNode* _underlineNode; + bool _strikethroughEnabled; + private: CC_DISALLOW_COPY_AND_ASSIGN(Label); }; diff --git a/cocos/base/ccTypes.h b/cocos/base/ccTypes.h index 9152eefb29..b5d60cd45d 100644 --- a/cocos/base/ccTypes.h +++ b/cocos/base/ccTypes.h @@ -570,14 +570,19 @@ public: }; /** - * @brief Possible LabelEffect used by Label. + * @brief Effects used by `Label` * */ enum class LabelEffect { + // FIXME: Covert them to bitwise. More than one effect should be supported NORMAL, OUTLINE, SHADOW, GLOW, + ITALICS, + BOLD, + UNDERLINE, + STRIKETHROUGH, ALL }; diff --git a/cocos/ui/UIRichText.cpp b/cocos/ui/UIRichText.cpp index 1dd1e9f603..824468f04a 100644 --- a/cocos/ui/UIRichText.cpp +++ b/cocos/ui/UIRichText.cpp @@ -23,7 +23,15 @@ ****************************************************************************/ #include "UIRichText.h" + +#include + +#include "tinyxml2/tinyxml2.h" #include "platform/CCFileUtils.h" +#include "platform/CCApplication.h" +#include "base/CCEventListenerTouch.h" +#include "base/CCEventDispatcher.h" +#include "base/CCDirector.h" #include "2d/CCLabel.h" #include "2d/CCSprite.h" #include "base/ccUTF8.h" @@ -32,8 +40,52 @@ NS_CC_BEGIN namespace ui { + class ListenerComponent : public Component + { + public: + static ListenerComponent* create(Label* parent, const std::string& url) + { + auto component = new (std::nothrow) ListenerComponent(parent, url); + component->autorelease(); + return component; + } + + explicit ListenerComponent(Label* parent, const std::string& url) + : _parent(parent) + , _url(url) + { + _touchListener = cocos2d::EventListenerTouchAllAtOnce::create(); + _touchListener->onTouchesEnded = CC_CALLBACK_2(ListenerComponent::onTouchesEnded, this); + + Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(_touchListener, _parent); + _touchListener->retain(); + } + + virtual ~ListenerComponent() + { + Director::getInstance()->getEventDispatcher()->removeEventListener(_touchListener); + _touchListener->release(); + } + + void onTouchesEnded(const std::vector& touches, Event *event) + { + for (const auto& touch: touches) + { + // FIXME: Node::getBoundBox() doesn't return it in local coordinates... so create one manually. + Rect localRect = Rect(Vec2::ZERO, _parent->getContentSize()); + if (localRect.containsPoint(_parent->convertTouchToNodeSpace(touch))) + Application::getInstance()->openURL(_url); + } + } + + private: + Label* _parent; // weak ref. + std::string _url; + EventDispatcher* _eventDispatcher; // weak ref. + EventListenerTouchAllAtOnce* _touchListener; // strong ref. + }; + - bool RichElement::init(int tag, const Color3B &color, GLubyte opacity) { _tag = tag; @@ -43,10 +95,10 @@ bool RichElement::init(int tag, const Color3B &color, GLubyte opacity) } -RichElementText* RichElementText::create(int tag, const Color3B &color, GLubyte opacity, const std::string& text, const std::string& fontName, float fontSize) +RichElementText* RichElementText::create(int tag, const Color3B &color, GLubyte opacity, const std::string& text, const std::string& fontName, float fontSize, uint32_t flags, const std::string& url) { RichElementText* element = new (std::nothrow) RichElementText(); - if (element && element->init(tag, color, opacity, text, fontName, fontSize)) + if (element && element->init(tag, color, opacity, text, fontName, fontSize, flags, url)) { element->autorelease(); return element; @@ -55,13 +107,15 @@ RichElementText* RichElementText::create(int tag, const Color3B &color, GLubyte return nullptr; } -bool RichElementText::init(int tag, const Color3B &color, GLubyte opacity, const std::string& text, const std::string& fontName, float fontSize) +bool RichElementText::init(int tag, const Color3B &color, GLubyte opacity, const std::string& text, const std::string& fontName, float fontSize, uint32_t flags, const std::string& url) { if (RichElement::init(tag, color, opacity)) { _text = text; _fontName = fontName; _fontSize = fontSize; + _flags = flags; + _url = url; return true; } return false; @@ -84,10 +138,21 @@ bool RichElementImage::init(int tag, const Color3B &color, GLubyte opacity, cons if (RichElement::init(tag, color, opacity)) { _filePath = filePath; + _width = -1; + _height = -1; return true; } return false; } +void RichElementImage::setWidth(int width) +{ + _width = width; +} + +void RichElementImage::setHeight(int height) +{ + _height = height; +} RichElementCustomNode* RichElementCustomNode::create(int tag, const Color3B &color, GLubyte opacity, cocos2d::Node *customNode) { @@ -148,6 +213,18 @@ RichText* RichText::create() CC_SAFE_DELETE(widget); return nullptr; } + +RichText* RichText::createWithXML(const std::string& xml) +{ + RichText* widget = new (std::nothrow) RichText(); + if (widget && widget->initWithXML(xml)) + { + widget->autorelease(); + return widget; + } + CC_SAFE_DELETE(widget); + return nullptr; +} bool RichText::init() { @@ -157,6 +234,307 @@ bool RichText::init() } return false; } + +class MyXMLVisitor: public tinyxml2::XMLVisitor +{ + struct Attributes + { + std::string face; + std::string url; + float fontSize; + Color3B color; + bool hasColor; + bool bold; + bool italics; + bool underline; + bool strikethrough; + + void setColor(const Color3B& acolor) + { + color = acolor; + hasColor = true; + } + Attributes() + : bold(false) + , italics(false) + , underline(false) + , strikethrough(false) + , hasColor(false) + , fontSize(-1) + { + } + }; + + std::vector _fontElements; + + RichText* _richText; + + Color3B getColor() const + { + for (auto i = _fontElements.rbegin(); i != _fontElements.rend(); ++i) + { + if (i->hasColor) + return i->color; + } + return Color3B::WHITE; + } + + float getFontSize() const + { + for (auto i = _fontElements.rbegin(); i != _fontElements.rend(); ++i) + { + if (i->fontSize != -1) + return i->fontSize; + } + return 12; + } + + std::string getFace() const + { + for (auto i = _fontElements.rbegin(); i != _fontElements.rend(); ++i) + { + if (i->face.size() != 0) + return i->face; + } + return "fonts/Marker Felt.ttf"; + } + + std::string getURL() const + { + for (auto i = _fontElements.rbegin(); i != _fontElements.rend(); ++i) + { + if (i->url.size() != 0) + return i->url; + } + return ""; + } + + bool getBold() const + { + for (auto i = _fontElements.rbegin(); i != _fontElements.rend(); ++i) + { + if (i->bold) + return true; + } + return false; + } + + bool getItalics() const + { + for (auto i = _fontElements.rbegin(); i != _fontElements.rend(); ++i) + { + if (i->italics) + return true; + } + return false; + } + + bool getUnderline() const + { + for (auto i = _fontElements.rbegin(); i != _fontElements.rend(); ++i) + { + if (i->underline) + return true; + } + return false; + } + + bool getStrikethrough() const + { + for (auto i = _fontElements.rbegin(); i != _fontElements.rend(); ++i) + { + if (i->strikethrough) + return true; + } + return false; + } + +public: + explicit MyXMLVisitor(RichText* richText) + : _richText(richText) + , _fontElements(20) + {} + virtual ~MyXMLVisitor() {} + + /// Visit an element. + virtual bool VisitEnter( const tinyxml2::XMLElement& element, const tinyxml2::XMLAttribute* firstAttribute ) + { + auto elementName = element.Value(); + + if (strcmp(elementName, "font") == 0) + { + // supported attributes: + // size, color, align, face + auto size = element.Attribute("size"); + auto color = element.Attribute("color"); + auto face = element.Attribute("face"); + + Attributes attribs; + if (size) + attribs.fontSize = atof(size); + if (color) + { + if (strlen(color) == 7) + { + int r,g,b; + sscanf(color, "%*c%2x%2x%2x", &r, &g, &b); + attribs.setColor(Color3B(r,g,b)); + } + else + attribs.setColor(Color3B::WHITE); + } + if (face) + attribs.face = face; + + _fontElements.push_back(attribs); + } + else if (strcmp(elementName, "b") == 0) + { + // no supported attributes + Attributes attribs; + attribs.bold = 1; + _fontElements.push_back(attribs); + } + else if (strcmp(elementName, "i") == 0) + { + // no supported attributes + Attributes attribs; + attribs.italics = 1; + _fontElements.push_back(attribs); + } + else if (strcmp(elementName, "del") == 0) + { + // no supported attributes + Attributes attribs; + attribs.strikethrough = true; + _fontElements.push_back(attribs); + } + else if (strcmp(elementName, "u") == 0) + { + // no supported attributes + Attributes attribs; + attribs.underline = true; + _fontElements.push_back(attribs); + } + else if (strcmp(elementName, "small") == 0) + { + Attributes attribs; + attribs.fontSize = getFontSize() * 0.8; + _fontElements.push_back(attribs); + } + + else if (strcmp(elementName, "big") == 0) + { + Attributes attribs; + attribs.fontSize = getFontSize() * 1.25; + _fontElements.push_back(attribs); + } + + else if (strcmp(elementName, "img") == 0) + { + // supported attributes: + // src, height, width + auto src = element.Attribute("src"); + auto height = element.Attribute("height"); + auto width = element.Attribute("width"); + + if (src) { + auto elementNL = RichElementImage::create(0, getColor(), 255, src); + + if (height) + elementNL->setHeight(atoi(height)); + if (width) + elementNL->setWidth(atoi(width)); + + _richText->pushBackElement(elementNL); + } + } + else if (strcmp(elementName, "a") == 0) + { + // supported attributes: + Attributes attribs; + auto href = element.Attribute("href"); + attribs.setColor(Color3B::BLUE); + attribs.underline = true; + attribs.url = href; + _fontElements.push_back(attribs); + } + else if (strcmp(elementName, "br") == 0) + { + auto color = getColor(); + auto elementNL = RichElementNewLine::create(0, color, 255); + _richText->pushBackElement(elementNL); + } + return true; + } + /// Visit an element. + virtual bool VisitExit( const tinyxml2::XMLElement& element ) + { + auto elementName = element.Value(); + if ((strcmp(elementName, "font") == 0) || + (strcmp(elementName, "i") == 0) || + (strcmp(elementName, "b") == 0) || + (strcmp(elementName, "del") == 0) || + (strcmp(elementName, "u") == 0) || + (strcmp(elementName, "small") == 0) || + (strcmp(elementName, "big") == 0) || + (strcmp(elementName, "a") == 0)) + { + _fontElements.pop_back(); + } + return true; + } + + /// Visit a text node. + virtual bool Visit(const tinyxml2::XMLText& text) + { + auto color = getColor(); + auto face = getFace(); + auto fontSize = getFontSize(); + auto italics = getItalics(); + auto underline = getUnderline(); + auto strikethrough = getStrikethrough(); + auto bold = getBold(); + auto url = getURL(); + + uint32_t flags = 0; + if (italics) + flags |= RichElementText::ITALICS_FLAG; + if (bold) + flags |= RichElementText::BOLD_FLAG; + if (underline) + flags |= RichElementText::UNDERLINE_FLAG; + if (strikethrough) + flags |= RichElementText::STRIKETHROUGH_FLAG; + if (url.size() > 0) + flags |= RichElementText::URL_FLAG; + + auto element = RichElementText::create(0, color, 255, text.Value(), face, fontSize, flags, url); + _richText->pushBackElement(element); + return true; + } +}; + +bool RichText::initWithXML(const std::string& origxml) +{ + if (Widget::init()) + { + tinyxml2::XMLDocument document; + + // solves to issues: + // - creates defaults values + // - makes sure that the xml well formed and starts with an element + auto xml = "" + origxml + ""; + + if (document.Parse(xml.c_str(), xml.length()) == tinyxml2::XML_SUCCESS) + { + MyXMLVisitor visitor(this); + document.Accept(&visitor); + return true; + } + } + return false; +} void RichText::initRenderer() { @@ -204,20 +582,42 @@ void RichText::formatText() case RichElement::Type::TEXT: { RichElementText* elmtText = static_cast(element); + Label* label; if (FileUtils::getInstance()->isFileExist(elmtText->_fontName)) { - elementRenderer = Label::createWithTTF(elmtText->_text.c_str(), elmtText->_fontName, elmtText->_fontSize); + label = Label::createWithTTF(elmtText->_text, elmtText->_fontName, elmtText->_fontSize); } else { - elementRenderer = Label::createWithSystemFont(elmtText->_text.c_str(), elmtText->_fontName, elmtText->_fontSize); + label = Label::createWithSystemFont(elmtText->_text, elmtText->_fontName, elmtText->_fontSize); } + if (elmtText->_flags & RichElementText::ITALICS_FLAG) + label->enableItalics(); + if (elmtText->_flags & RichElementText::BOLD_FLAG) + label->enableBold(); + if (elmtText->_flags & RichElementText::UNDERLINE_FLAG) + label->enableUnderline(); + if (elmtText->_flags & RichElementText::STRIKETHROUGH_FLAG) + label->enableStrikethrough(); + if (elmtText->_flags & RichElementText::URL_FLAG) + label->addComponent(ListenerComponent::create(label, elmtText->_url)); + elementRenderer = label; break; } case RichElement::Type::IMAGE: { RichElementImage* elmtImage = static_cast(element); - elementRenderer = Sprite::create(elmtImage->_filePath.c_str()); + elementRenderer = Sprite::create(elmtImage->_filePath); + if (elementRenderer && (elmtImage->_height != -1 || elmtImage->_width != -1)) + { + auto currentSize = elementRenderer->getContentSize(); + if (elmtImage->_width != -1) + elementRenderer->setScaleX(elmtImage->_width / currentSize.width); + if (elmtImage->_height != -1) + elementRenderer->setScaleY(elmtImage->_height / currentSize.height); + elementRenderer->setContentSize(Size(currentSize.width * elementRenderer->getScaleX(), + currentSize.height * elementRenderer->getScaleY())); + } break; } case RichElement::Type::CUSTOM: @@ -234,9 +634,13 @@ void RichText::formatText() default: break; } - elementRenderer->setColor(element->_color); - elementRenderer->setOpacity(element->_opacity); - pushToContainer(elementRenderer); + + if (elementRenderer) + { + elementRenderer->setColor(element->_color); + elementRenderer->setOpacity(element->_opacity); + pushToContainer(elementRenderer); + } } } else @@ -244,20 +648,19 @@ void RichText::formatText() addNewLine(); for (ssize_t i=0; i<_richElements.size(); i++) { - RichElement* element = static_cast(_richElements.at(i)); switch (element->_type) { case RichElement::Type::TEXT: { RichElementText* elmtText = static_cast(element); - handleTextRenderer(elmtText->_text.c_str(), elmtText->_fontName.c_str(), elmtText->_fontSize, elmtText->_color, elmtText->_opacity); + handleTextRenderer(elmtText->_text, elmtText->_fontName, elmtText->_fontSize, elmtText->_color, elmtText->_opacity, elmtText->_flags, elmtText->_url); break; } case RichElement::Type::IMAGE: { RichElementImage* elmtImage = static_cast(element); - handleImageRenderer(elmtImage->_filePath.c_str(), elmtImage->_color, elmtImage->_opacity); + handleImageRenderer(elmtImage->_filePath, elmtImage->_color, elmtImage->_opacity, elmtImage->_width, elmtImage->_height); break; } case RichElement::Type::CUSTOM: @@ -280,8 +683,8 @@ void RichText::formatText() _formatTextDirty = false; } } - -void RichText::handleTextRenderer(const std::string& text, const std::string& fontName, float fontSize, const Color3B &color, GLubyte opacity) + +void RichText::handleTextRenderer(const std::string& text, const std::string& fontName, float fontSize, const Color3B &color, GLubyte opacity, uint32_t flags, const std::string& url) { auto fileExist = FileUtils::getInstance()->isFileExist(fontName); Label* textRenderer = nullptr; @@ -293,6 +696,17 @@ void RichText::handleTextRenderer(const std::string& text, const std::string& fo { textRenderer = Label::createWithSystemFont(text, fontName, fontSize); } + if (flags & RichElementText::ITALICS_FLAG) + textRenderer->enableItalics(); + if (flags & RichElementText::BOLD_FLAG) + textRenderer->enableBold(); + if (flags & RichElementText::UNDERLINE_FLAG) + textRenderer->enableUnderline(); + if (flags & RichElementText::STRIKETHROUGH_FLAG) + textRenderer->enableStrikethrough(); + if (flags & RichElementText::URL_FLAG) + textRenderer->addComponent(ListenerComponent::create(textRenderer, url)); + float textRendererWidth = textRenderer->getContentSize().width; _leftSpaceWidth -= textRendererWidth; if (_leftSpaceWidth < 0.0f) @@ -359,11 +773,23 @@ void RichText::handleTextRenderer(const std::string& text, const std::string& fo leftRenderer->setColor(color); leftRenderer->setOpacity(opacity); pushToContainer(leftRenderer); + + if (flags & RichElementText::ITALICS_FLAG) + leftRenderer->enableItalics(); + if (flags & RichElementText::BOLD_FLAG) + leftRenderer->enableBold(); + if (flags & RichElementText::UNDERLINE_FLAG) + leftRenderer->enableUnderline(); + if (flags & RichElementText::STRIKETHROUGH_FLAG) + leftRenderer->enableStrikethrough(); + if (flags & RichElementText::URL_FLAG) + leftRenderer->addComponent(ListenerComponent::create(leftRenderer, url)); + } } addNewLine(); - handleTextRenderer(cutWords.c_str(), fontName, fontSize, color, opacity); + handleTextRenderer(cutWords, fontName, fontSize, color, opacity, flags, url); } else { @@ -373,12 +799,23 @@ void RichText::handleTextRenderer(const std::string& text, const std::string& fo } } -void RichText::handleImageRenderer(const std::string& fileParh, const Color3B &color, GLubyte opacity) +void RichText::handleImageRenderer(const std::string& filePath, const Color3B &color, GLubyte opacity, int width, int height) { - Sprite* imageRenderer = Sprite::create(fileParh); + Sprite* imageRenderer = Sprite::create(filePath); + if (imageRenderer) + { + auto currentSize = imageRenderer->getContentSize(); + if (width != -1) + imageRenderer->setScaleX(width / currentSize.width); + if (height != -1) + imageRenderer->setScaleY(height / currentSize.height); + imageRenderer->setContentSize(Size(currentSize.width * imageRenderer->getScaleX(), + currentSize.height * imageRenderer->getScaleY())); + } + handleCustomRenderer(imageRenderer); } - + void RichText::handleCustomRenderer(cocos2d::Node *renderer) { Size imgSize = renderer->getContentSize(); @@ -406,22 +843,26 @@ void RichText::formarRenderers() if (_ignoreSize) { float newContentSizeWidth = 0.0f; - float newContentSizeHeight = 0.0f; - - Vector* row = (_elementRenders[0]); - float nextPosX = 0.0f; - for (ssize_t j=0; jsize(); j++) + float nextPosY = 0.0f; + for (auto& element: _elementRenders) { - Node* l = row->at(j); - l->setAnchorPoint(Vec2::ZERO); - l->setPosition(nextPosX, 0.0f); - this->addProtectedChild(l, 1); - Size iSize = l->getContentSize(); - newContentSizeWidth += iSize.width; - newContentSizeHeight = MAX(newContentSizeHeight, iSize.height); - nextPosX += iSize.width; + Vector* row = element; + float nextPosX = 0.0f; + float maxY = 0.0f; + for (ssize_t j=0; jsize(); j++) + { + Node* l = row->at(j); + l->setAnchorPoint(Vec2::ZERO); + l->setPosition(nextPosX, nextPosY); + this->addProtectedChild(l, 1); + Size iSize = l->getContentSize(); + newContentSizeWidth += iSize.width; + nextPosX += iSize.width; + maxY = MAX(maxY, iSize.height); + } + nextPosY -= maxY; } - this->setContentSize(Size(newContentSizeWidth, newContentSizeHeight)); + this->setContentSize(Size(newContentSizeWidth, -nextPosY)); } else { diff --git a/cocos/ui/UIRichText.h b/cocos/ui/UIRichText.h index 37d24a2774..a5ce9f2410 100644 --- a/cocos/ui/UIRichText.h +++ b/cocos/ui/UIRichText.h @@ -98,8 +98,16 @@ public: * @js ctor * @lua new */ - RichElementText(){_type = Type::TEXT;}; + RichElementText() + {_type = Type::TEXT;}; + enum { + ITALICS_FLAG = 1 << 0, + BOLD_FLAG = 1 << 1, + UNDERLINE_FLAG = 1 << 2, + STRIKETHROUGH_FLAG = 1 << 3, + URL_FLAG = 1 << 4 + }; /** *@brief Default destructor. @@ -117,9 +125,10 @@ public: * @param text Content string. * @param fontName Content font name. * @param fontSize Content font size. + * @param flags: italics, bold, underline or strikethrough * @return True if initialize success, false otherwise. */ - bool init(int tag, const Color3B& color, GLubyte opacity, const std::string& text, const std::string& fontName, float fontSize); + bool init(int tag, const Color3B& color, GLubyte opacity, const std::string& text, const std::string& fontName, float fontSize, uint32_t flags, const std::string& url); /** @@ -131,13 +140,17 @@ public: * @param text Content string. * @param fontName Content font name. * @param fontSize Content font size. + * @param flags: italics, bold, underline or strikethrough * @return RichElementText instance. */ - static RichElementText* create(int tag, const Color3B& color, GLubyte opacity, const std::string& text, const std::string& fontName, float fontSize); + static RichElementText* create(int tag, const Color3B& color, GLubyte opacity, const std::string& text, + const std::string& fontName, float fontSize, uint32_t flags=0, const std::string& url=""); protected: std::string _text; std::string _fontName; float _fontSize; + uint32_t _flags; + std::string _url; friend class RichText; }; @@ -188,11 +201,16 @@ public: * @return A RichElementImage instance. */ static RichElementImage* create(int tag, const Color3B& color, GLubyte opacity, const std::string& filePath); + + void setWidth(int width); + void setHeight(int height); protected: std::string _filePath; Rect _textureRect; int _textureType; friend class RichText; + int _width; + int _height; }; /** @@ -307,7 +325,14 @@ public: * @return RichText instance. */ static RichText* create(); - + + /** + * @brief Create a RichText from an XML + * + * @return RichText instance. + */ + static RichText* createWithXML(const std::string& xml); + /** * @brief Insert a RichElement at a given index. * @@ -356,14 +381,16 @@ public: CC_CONSTRUCTOR_ACCESS: virtual bool init() override; - + + bool initWithXML(const std::string& xml); + protected: virtual void adaptRenderers() override; virtual void initRenderer() override; void pushToContainer(Node* renderer); - void handleTextRenderer(const std::string& text, const std::string& fontName, float fontSize, const Color3B& color, GLubyte opacity); - void handleImageRenderer(const std::string& fileParh, const Color3B& color, GLubyte opacity); + void handleTextRenderer(const std::string& text, const std::string& fontName, float fontSize, const Color3B& color, GLubyte opacity, uint32_t flags, const std::string& url=""); + void handleImageRenderer(const std::string& fileParh, const Color3B& color, GLubyte opacity, int width, int height); void handleCustomRenderer(Node* renderer); void formarRenderers(); void addNewLine(); diff --git a/tests/cpp-tests/Classes/LabelTest/LabelTestNew.cpp b/tests/cpp-tests/Classes/LabelTest/LabelTestNew.cpp index 2898d4c3cf..7410b5e7c7 100644 --- a/tests/cpp-tests/Classes/LabelTest/LabelTestNew.cpp +++ b/tests/cpp-tests/Classes/LabelTest/LabelTestNew.cpp @@ -98,6 +98,13 @@ NewLabelTests::NewLabelTests() ADD_TEST_CASE(LabelSystemFontTest); ADD_TEST_CASE(LabelCharMapFontTest); ADD_TEST_CASE(LabelIssue13846Test); + + ADD_TEST_CASE(LabelRichText); + ADD_TEST_CASE(LabelStrikethrough); + ADD_TEST_CASE(LabelUnderline); + ADD_TEST_CASE(LabelUnderlineMultiline); + ADD_TEST_CASE(LabelItalics); + ADD_TEST_CASE(LabelBold); }; LabelFNTColorAndOpacity::LabelFNTColorAndOpacity() @@ -2781,3 +2788,268 @@ std::string LabelIssue13846Test::subtitle() const { return "Test hide label's letter,the label should display ‘12 45’ as expected"; } + +// +// + +LabelRichText::LabelRichText() +{ + auto center = VisibleRect::center(); + + auto richText2 = RichText::createWithXML("Mixing UIRichText with non UIWidget code. For more samples, see the UIRichTextTest.cpp file"); + if (richText2) + { + richText2->ignoreContentAdaptWithSize(false); + richText2->setContentSize(Size(400, 400)); + + addChild(richText2); + richText2->setPosition(Vec2(200,0)); + } +} + +std::string LabelRichText::title() const +{ + return "RichText"; +} + +std::string LabelRichText::subtitle() const +{ + return "Testing RichText"; +} + +LabelItalics::LabelItalics() +{ + auto s = Director::getInstance()->getWinSize(); + + // LabelBMFont + auto label1 = Label::createWithBMFont("fonts/bitmapFontTest2.fnt", "hello non-italics", TextHAlignment::CENTER, s.width); + addChild(label1, 0, kTagBitmapAtlas1); + label1->setPosition(Vec2(s.width/2, s.height*4/6)); + // you can enable italics by calling this method + + _label1a = Label::createWithBMFont("fonts/bitmapFontTest2.fnt", "hello italics", TextHAlignment::CENTER, s.width); + addChild(_label1a, 0, kTagBitmapAtlas1); + _label1a->setPosition(Vec2(s.width/2, s.height*3/6)); + // you can enable italics by calling this method + _label1a->enableItalics(); + + + // LabelTTF + TTFConfig ttfConfig("fonts/arial.ttf",24); + auto label2 = Label::createWithTTF(ttfConfig, "hello non-italics", TextHAlignment::CENTER,s.width); + addChild(label2, 0, kTagBitmapAtlas2); + label2->setPosition(Vec2(s.width/2, s.height*2/6)); + + // or by setting the italics parameter on TTFConfig + ttfConfig.italics = true; + _label2a = Label::createWithTTF(ttfConfig, "hello italics", TextHAlignment::CENTER,s.width); + addChild(_label2a, 0, kTagBitmapAtlas2); + _label2a->setPosition(Vec2(s.width/2, s.height*1/6)); + + auto menuItem = MenuItemFont::create("disable italics", [&](cocos2d::Ref* sender) { + _label2a->disableEffect(LabelEffect::ITALICS); + _label1a->disableEffect(LabelEffect::ITALICS); + }); + menuItem->setFontSizeObj(12); + auto menu = Menu::createWithItem(menuItem); + addChild(menu); + auto winSize = Director::getInstance()->getWinSize(); + menu->setPosition(winSize.width * 0.9, winSize.height * 0.25f); +} + +std::string LabelItalics::title() const +{ + return "Testing Italics"; +} + +std::string LabelItalics::subtitle() const +{ + return "italics on TTF and BMfont"; +} + +/// + +LabelBold::LabelBold() +{ + auto s = Director::getInstance()->getWinSize(); + + // LabelBMFont + auto label1 = Label::createWithBMFont("fonts/bitmapFontTest2.fnt", "hello non-bold", TextHAlignment::CENTER, s.width); + addChild(label1, 0, kTagBitmapAtlas1); + label1->setPosition(Vec2(s.width/2, s.height*4/6)); + // you can enable italics by calling this method + + _label1a = Label::createWithBMFont("fonts/bitmapFontTest2.fnt", "hello bold", TextHAlignment::CENTER, s.width); + addChild(_label1a, 0, kTagBitmapAtlas1); + _label1a->setPosition(Vec2(s.width/2, s.height*3/6)); + // you can enable italics by calling this method + _label1a->enableBold(); + + + // LabelTTF + TTFConfig ttfConfig("fonts/arial.ttf",24); + auto label2 = Label::createWithTTF(ttfConfig, "hello non-bold", TextHAlignment::CENTER,s.width); + addChild(label2, 0, kTagBitmapAtlas2); + label2->setPosition(Vec2(s.width/2, s.height*2/6)); + + // or by setting the italics parameter on TTFConfig + ttfConfig.bold = true; + _label2a = Label::createWithTTF(ttfConfig, "hello bold", TextHAlignment::CENTER,s.width); + addChild(_label2a, 0, kTagBitmapAtlas2); + _label2a->setPosition(Vec2(s.width/2, s.height*1/6)); + + auto menuItem = MenuItemFont::create("disable bold", [&](cocos2d::Ref* sender) { + _label2a->disableEffect(LabelEffect::BOLD); + _label1a->disableEffect(LabelEffect::BOLD); + }); + menuItem->setFontSizeObj(12); + auto menu = Menu::createWithItem(menuItem); + addChild(menu); + auto winSize = Director::getInstance()->getWinSize(); + menu->setPosition(winSize.width * 0.9, winSize.height * 0.25f); +} + +std::string LabelBold::title() const +{ + return "Testing Bold"; +} + +std::string LabelBold::subtitle() const +{ + return "Bold on TTF and BMfont"; +} + +/// + +LabelUnderline::LabelUnderline() +{ + auto s = Director::getInstance()->getWinSize(); + + // LabelBMFont + auto label1 = Label::createWithBMFont("fonts/bitmapFontTest2.fnt", "hello non-underline", TextHAlignment::CENTER, s.width); + addChild(label1, 0, kTagBitmapAtlas1); + label1->setPosition(Vec2(s.width/2, s.height*4/6)); + // you can enable italics by calling this method + + _label1a = Label::createWithBMFont("fonts/bitmapFontTest2.fnt", "hello underline", TextHAlignment::CENTER, s.width); + addChild(_label1a, 0, kTagBitmapAtlas1); + _label1a->setPosition(Vec2(s.width/2, s.height*3/6)); + // you can enable underline by calling this method + _label1a->enableUnderline(); + + + // LabelTTF + TTFConfig ttfConfig("fonts/arial.ttf",24); + auto label2 = Label::createWithTTF(ttfConfig, "hello non-underline", TextHAlignment::CENTER,s.width); + addChild(label2, 0, kTagBitmapAtlas2); + label2->setPosition(Vec2(s.width/2, s.height*2/6)); + + // or by setting the italics parameter on TTFConfig + ttfConfig.underline = true; + _label2a = Label::createWithTTF(ttfConfig, "hello underline", TextHAlignment::CENTER,s.width); + addChild(_label2a, 0, kTagBitmapAtlas2); + _label2a->setPosition(Vec2(s.width/2, s.height*1/6)); + + auto menuItem = MenuItemFont::create("disable underline", [&](cocos2d::Ref* sender) { + _label2a->disableEffect(LabelEffect::UNDERLINE); + _label1a->disableEffect(LabelEffect::UNDERLINE); + }); + menuItem->setFontSizeObj(12); + auto menu = Menu::createWithItem(menuItem); + addChild(menu); + auto winSize = Director::getInstance()->getWinSize(); + menu->setPosition(winSize.width * 0.9, winSize.height * 0.25f); +} + +std::string LabelUnderline::title() const +{ + return "Testing Underline"; +} + +std::string LabelUnderline::subtitle() const +{ + return "Underline on TTF and BMfont"; +} + +/// + +LabelUnderlineMultiline::LabelUnderlineMultiline() +{ + auto s = Director::getInstance()->getWinSize(); + + // bmfont + _label1a = Label::createWithBMFont("fonts/bitmapFontTest5.fnt", "hello underline\nand multiline", TextHAlignment::CENTER, s.width); + addChild(_label1a, 0, kTagBitmapAtlas1); + _label1a->setPosition(Vec2(s.width/2, s.height*2/3)); + // you can enable underline by calling this method + _label1a->enableUnderline(); + + // ttf + TTFConfig ttfConfig("fonts/arial.ttf",24); + ttfConfig.underline = true; + _label2a = Label::createWithTTF(ttfConfig, "hello\nunderline\nwith multiline", TextHAlignment::LEFT, s.width); + addChild(_label2a, 0, kTagBitmapAtlas2); + _label2a->setPosition(Vec2(s.width/2, s.height*1/3)); + + auto menuItem = MenuItemFont::create("disable underline", [&](cocos2d::Ref* sender) { + _label2a->disableEffect(LabelEffect::UNDERLINE); + _label1a->disableEffect(LabelEffect::UNDERLINE); + }); + menuItem->setFontSizeObj(12); + auto menu = Menu::createWithItem(menuItem); + addChild(menu); + auto winSize = Director::getInstance()->getWinSize(); + menu->setPosition(winSize.width * 0.9, winSize.height * 0.25f); +} + +std::string LabelUnderlineMultiline::title() const +{ + return "Testing Underline + multiline"; +} + +std::string LabelUnderlineMultiline::subtitle() const +{ + return "Underline on TTF and BMfont with multiline"; +} + +/// + +LabelStrikethrough::LabelStrikethrough() +{ + auto s = Director::getInstance()->getWinSize(); + + // bmfont + _label1a = Label::createWithBMFont("fonts/bitmapFontTest4.fnt", "hello strikethrough\nand multiline", TextHAlignment::LEFT, s.width); + addChild(_label1a, 0, kTagBitmapAtlas1); + _label1a->setPosition(Vec2(s.width/2, s.height*2/3)); + // you can enable underline by calling this method + _label1a->enableStrikethrough(); + + // ttf + TTFConfig ttfConfig("fonts/arial.ttf",24); + ttfConfig.strikethrough = true; + _label2a = Label::createWithTTF(ttfConfig, "hello\nstrikethrough\nwith multiline", TextHAlignment::RIGHT, s.width); + addChild(_label2a, 0, kTagBitmapAtlas2); + _label2a->setPosition(Vec2(s.width/2, s.height*1/3)); + + auto menuItem = MenuItemFont::create("disable underline", [&](cocos2d::Ref* sender) { + _label2a->disableEffect(LabelEffect::STRIKETHROUGH); + _label1a->disableEffect(LabelEffect::STRIKETHROUGH); + }); + menuItem->setFontSizeObj(12); + auto menu = Menu::createWithItem(menuItem); + addChild(menu); + auto winSize = Director::getInstance()->getWinSize(); + menu->setPosition(winSize.width * 0.9, winSize.height * 0.25f); +} + +std::string LabelStrikethrough::title() const +{ + return "Testing Strikethrough + multiline"; +} + +std::string LabelStrikethrough::subtitle() const +{ + return "Strikethrough on TTF and BMfont with multiline"; +} + diff --git a/tests/cpp-tests/Classes/LabelTest/LabelTestNew.h b/tests/cpp-tests/Classes/LabelTest/LabelTestNew.h index bed615476c..5374eea23e 100644 --- a/tests/cpp-tests/Classes/LabelTest/LabelTestNew.h +++ b/tests/cpp-tests/Classes/LabelTest/LabelTestNew.h @@ -767,4 +767,79 @@ public: virtual std::string subtitle() const override; }; +class LabelRichText : public AtlasDemoNew +{ +public: + CREATE_FUNC(LabelRichText); + + LabelRichText(); + virtual std::string title() const override; + virtual std::string subtitle() const override; +}; + +class LabelItalics : public AtlasDemoNew +{ +public: + CREATE_FUNC(LabelItalics); + + LabelItalics(); + virtual std::string title() const override; + virtual std::string subtitle() const override; + + cocos2d::Label* _label1a; + cocos2d::Label* _label2a; +}; + +class LabelBold : public AtlasDemoNew +{ +public: + CREATE_FUNC(LabelBold); + + LabelBold(); + virtual std::string title() const override; + virtual std::string subtitle() const override; + + cocos2d::Label* _label1a; + cocos2d::Label* _label2a; +}; + +class LabelUnderline : public AtlasDemoNew +{ +public: + CREATE_FUNC(LabelUnderline); + + LabelUnderline(); + virtual std::string title() const override; + virtual std::string subtitle() const override; + + cocos2d::Label* _label1a; + cocos2d::Label* _label2a; +}; + +class LabelUnderlineMultiline : public AtlasDemoNew +{ +public: + CREATE_FUNC(LabelUnderlineMultiline); + + LabelUnderlineMultiline(); + virtual std::string title() const override; + virtual std::string subtitle() const override; + + cocos2d::Label* _label1a; + cocos2d::Label* _label2a; +}; + +class LabelStrikethrough : public AtlasDemoNew +{ +public: + CREATE_FUNC(LabelStrikethrough); + + LabelStrikethrough(); + virtual std::string title() const override; + virtual std::string subtitle() const override; + + cocos2d::Label* _label1a; + cocos2d::Label* _label2a; +}; + #endif diff --git a/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/UIRichTextTest/UIRichTextTest.cpp b/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/UIRichTextTest/UIRichTextTest.cpp index 93d0d11661..0cc13bc70e 100644 --- a/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/UIRichTextTest/UIRichTextTest.cpp +++ b/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/UIRichTextTest/UIRichTextTest.cpp @@ -8,18 +8,22 @@ using namespace cocos2d::ui; UIRichTextTests::UIRichTextTests() { ADD_TEST_CASE(UIRichTextTest); + ADD_TEST_CASE(UIRichTextXMLBasic); + ADD_TEST_CASE(UIRichTextXMLSmallBig); + ADD_TEST_CASE(UIRichTextXMLColor); + ADD_TEST_CASE(UIRichTextXMLSUIB); + ADD_TEST_CASE(UIRichTextXMLSUIB2); + ADD_TEST_CASE(UIRichTextXMLSUIB3); + ADD_TEST_CASE(UIRichTextXMLImg); + ADD_TEST_CASE(UIRichTextXMLUrl); + ADD_TEST_CASE(UIRichTextXMLFace); + ADD_TEST_CASE(UIRichTextXMLBR); } -UIRichTextTest::UIRichTextTest() -{ - -} - -UIRichTextTest::~UIRichTextTest() -{ - -} +// +// UIRichTextTest +// bool UIRichTextTest::init() { if (UIScene::init()) @@ -123,3 +127,673 @@ void UIRichTextTest::touchEvent(Ref *pSender, Widget::TouchEventType type) break; } } + +// +// UIRichTextXMLBasic +// +bool UIRichTextXMLBasic::init() +{ + if (UIScene::init()) + { + Size widgetSize = _widget->getContentSize(); + + // Add the alert + Text *alert = Text::create("RichText", "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); + + + Button* button = Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png"); + button->setTouchEnabled(true); + button->setTitleText("switch"); + button->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + button->getContentSize().height * 2.5)); + button->addTouchEventListener(CC_CALLBACK_2(UIRichTextXMLBasic::touchEvent, this)); + button->setLocalZOrder(10); + _widget->addChild(button); + + + // RichText + _richText = RichText::createWithXML("This is just a simple text. no xml tags here. testing the basics. testing word-wrapping. testing, testing, testing"); + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + + _richText->setPosition(Vec2(widgetSize.width / 2, widgetSize.height / 2)); + _richText->setLocalZOrder(10); + + + _widget->addChild(_richText); + + // test remove all children, this call won't effect the test + _richText->removeAllChildren(); + + return true; + } + return false; +} + +void UIRichTextXMLBasic::touchEvent(Ref *pSender, Widget::TouchEventType type) +{ + switch (type) + { + case Widget::TouchEventType::ENDED: + { + if (_richText->isIgnoreContentAdaptWithSize()) + { + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + } + else + { + _richText->ignoreContentAdaptWithSize(true); + } + } + break; + + default: + break; + } +} + +// +// UIRichTextXMLSmallBig +// +bool UIRichTextXMLSmallBig::init() +{ + if (UIScene::init()) + { + Size widgetSize = _widget->getContentSize(); + + // Add the alert + Text *alert = Text::create("RichText", "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); + + + Button* button = Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png"); + button->setTouchEnabled(true); + button->setTitleText("switch"); + button->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + button->getContentSize().height * 2.5)); + button->addTouchEventListener(CC_CALLBACK_2(UIRichTextXMLSmallBig::touchEvent, this)); + button->setLocalZOrder(10); + _widget->addChild(button); + + + // RichText + _richText = RichText::createWithXML("Regular size.smaller size.bigger.normal.bigger.normal."); + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + + _richText->setPosition(Vec2(widgetSize.width / 2, widgetSize.height / 2)); + _richText->setLocalZOrder(10); + + + _widget->addChild(_richText); + + // test remove all children, this call won't effect the test + _richText->removeAllChildren(); + + return true; + } + return false; +} + +void UIRichTextXMLSmallBig::touchEvent(Ref *pSender, Widget::TouchEventType type) +{ + switch (type) + { + case Widget::TouchEventType::ENDED: + { + if (_richText->isIgnoreContentAdaptWithSize()) + { + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + } + else + { + _richText->ignoreContentAdaptWithSize(true); + } + } + break; + + default: + break; + } +} + +// +// UIRichTextXMLColor +// +bool UIRichTextXMLColor::init() +{ + if (UIScene::init()) + { + Size widgetSize = _widget->getContentSize(); + + // Add the alert + Text *alert = Text::create("RichText", "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); + + + Button* button = Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png"); + button->setTouchEnabled(true); + button->setTitleText("switch"); + button->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + button->getContentSize().height * 2.5)); + button->addTouchEventListener(CC_CALLBACK_2(UIRichTextXMLColor::touchEvent, this)); + button->setLocalZOrder(10); + _widget->addChild(button); + + + // RichText + _richText = RichText::createWithXML("Defaul color.red.greenred again.default again"); + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + + _richText->setPosition(Vec2(widgetSize.width / 2, widgetSize.height / 2)); + _richText->setLocalZOrder(10); + + + _widget->addChild(_richText); + + // test remove all children, this call won't effect the test + _richText->removeAllChildren(); + + return true; + } + return false; +} + +void UIRichTextXMLColor::touchEvent(Ref *pSender, Widget::TouchEventType type) +{ + switch (type) + { + case Widget::TouchEventType::ENDED: + { + if (_richText->isIgnoreContentAdaptWithSize()) + { + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + } + else + { + _richText->ignoreContentAdaptWithSize(true); + } + } + break; + + default: + break; + } +} + +// +// UIRichTextXMLSUIB +// +bool UIRichTextXMLSUIB::init() +{ + if (UIScene::init()) + { + Size widgetSize = _widget->getContentSize(); + + // Add the alert + Text *alert = Text::create("RichText", "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); + + + Button* button = Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png"); + button->setTouchEnabled(true); + button->setTitleText("switch"); + button->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + button->getContentSize().height * 2.5)); + button->addTouchEventListener(CC_CALLBACK_2(UIRichTextXMLSUIB::touchEvent, this)); + button->setLocalZOrder(10); + _widget->addChild(button); + + + // RichText + _richText = RichText::createWithXML("system font: underlineitalicsboldstrike-through"); + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + + _richText->setPosition(Vec2(widgetSize.width / 2, widgetSize.height / 2)); + _richText->setLocalZOrder(10); + + + _widget->addChild(_richText); + + // test remove all children, this call won't effect the test + _richText->removeAllChildren(); + + return true; + } + return false; +} + +void UIRichTextXMLSUIB::touchEvent(Ref *pSender, Widget::TouchEventType type) +{ + switch (type) + { + case Widget::TouchEventType::ENDED: + { + if (_richText->isIgnoreContentAdaptWithSize()) + { + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + } + else + { + _richText->ignoreContentAdaptWithSize(true); + } + } + break; + + default: + break; + } +} + +// +// UIRichTextXMLSUIB2 +// +bool UIRichTextXMLSUIB2::init() +{ + if (UIScene::init()) + { + Size widgetSize = _widget->getContentSize(); + + // Add the alert + Text *alert = Text::create("RichText", "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); + + + Button* button = Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png"); + button->setTouchEnabled(true); + button->setTitleText("switch"); + button->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + button->getContentSize().height * 2.5)); + button->addTouchEventListener(CC_CALLBACK_2(UIRichTextXMLSUIB2::touchEvent, this)); + button->setLocalZOrder(10); + _widget->addChild(button); + + + // RichText + _richText = RichText::createWithXML("ttf font: underlineitalicsboldstrike-through"); + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + + _richText->setPosition(Vec2(widgetSize.width / 2, widgetSize.height / 2)); + _richText->setLocalZOrder(10); + + + _widget->addChild(_richText); + + // test remove all children, this call won't effect the test + _richText->removeAllChildren(); + + return true; + } + return false; +} + +void UIRichTextXMLSUIB2::touchEvent(Ref *pSender, Widget::TouchEventType type) +{ + switch (type) + { + case Widget::TouchEventType::ENDED: + { + if (_richText->isIgnoreContentAdaptWithSize()) + { + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + } + else + { + _richText->ignoreContentAdaptWithSize(true); + } + } + break; + + default: + break; + } +} + +// +// UIRichTextXMLSUIB3 +// +bool UIRichTextXMLSUIB3::init() +{ + if (UIScene::init()) + { + Size widgetSize = _widget->getContentSize(); + + // Add the alert + Text *alert = Text::create("RichText", "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); + + + Button* button = Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png"); + button->setTouchEnabled(true); + button->setTitleText("switch"); + button->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + button->getContentSize().height * 2.5)); + button->addTouchEventListener(CC_CALLBACK_2(UIRichTextXMLSUIB3::touchEvent, this)); + button->setLocalZOrder(10); + _widget->addChild(button); + + + // RichText + _richText = RichText::createWithXML("ttf font: italics and underlinebold and strike-through"); + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + + _richText->setPosition(Vec2(widgetSize.width / 2, widgetSize.height / 2)); + _richText->setLocalZOrder(10); + + + _widget->addChild(_richText); + + // test remove all children, this call won't effect the test + _richText->removeAllChildren(); + + return true; + } + return false; +} + +void UIRichTextXMLSUIB3::touchEvent(Ref *pSender, Widget::TouchEventType type) +{ + switch (type) + { + case Widget::TouchEventType::ENDED: + { + if (_richText->isIgnoreContentAdaptWithSize()) + { + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + } + else + { + _richText->ignoreContentAdaptWithSize(true); + } + } + break; + + default: + break; + } +} + +// +// UIRichTextXMLImg +// +bool UIRichTextXMLImg::init() +{ + if (UIScene::init()) + { + Size widgetSize = _widget->getContentSize(); + + // Add the alert + Text *alert = Text::create("RichText", "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); + + + Button* button = Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png"); + button->setTouchEnabled(true); + button->setTitleText("switch"); + button->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + button->getContentSize().height * 2.5)); + button->addTouchEventListener(CC_CALLBACK_2(UIRichTextXMLImg::touchEvent, this)); + button->setLocalZOrder(10); + _widget->addChild(button); + + + // RichText + _richText = RichText::createWithXML("you should see an image here: and this is text again. and this is the same image, but bigger: and here goes text again"); + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + + _richText->setPosition(Vec2(widgetSize.width / 2, widgetSize.height / 2)); + _richText->setLocalZOrder(10); + + + _widget->addChild(_richText); + + // test remove all children, this call won't effect the test + _richText->removeAllChildren(); + + return true; + } + return false; +} + +void UIRichTextXMLImg::touchEvent(Ref *pSender, Widget::TouchEventType type) +{ + switch (type) + { + case Widget::TouchEventType::ENDED: + { + if (_richText->isIgnoreContentAdaptWithSize()) + { + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + } + else + { + _richText->ignoreContentAdaptWithSize(true); + } + } + break; + + default: + break; + } +} + +// +// UIRichTextXMLUrl +// +bool UIRichTextXMLUrl::init() +{ + if (UIScene::init()) + { + Size widgetSize = _widget->getContentSize(); + + // Add the alert + Text *alert = Text::create("RichText", "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); + + + Button* button = Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png"); + button->setTouchEnabled(true); + button->setTitleText("switch"); + button->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + button->getContentSize().height * 2.5)); + button->addTouchEventListener(CC_CALLBACK_2(UIRichTextXMLUrl::touchEvent, this)); + button->setLocalZOrder(10); + _widget->addChild(button); + + + // RichText + _richText = RichText::createWithXML("And this link will redirect you to google: click me"); + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + + _richText->setPosition(Vec2(widgetSize.width / 2, widgetSize.height / 2)); + _richText->setLocalZOrder(10); + + + _widget->addChild(_richText); + + // test remove all children, this call won't effect the test + _richText->removeAllChildren(); + + return true; + } + return false; +} + +void UIRichTextXMLUrl::touchEvent(Ref *pSender, Widget::TouchEventType type) +{ + switch (type) + { + case Widget::TouchEventType::ENDED: + { + if (_richText->isIgnoreContentAdaptWithSize()) + { + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + } + else + { + _richText->ignoreContentAdaptWithSize(true); + } + } + break; + + default: + break; + } +} + +// +// UIRichTextXMLFace +// +bool UIRichTextXMLFace::init() +{ + if (UIScene::init()) + { + Size widgetSize = _widget->getContentSize(); + + // Add the alert + Text *alert = Text::create("RichText", "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); + + + Button* button = Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png"); + button->setTouchEnabled(true); + button->setTitleText("switch"); + button->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + button->getContentSize().height * 2.5)); + button->addTouchEventListener(CC_CALLBACK_2(UIRichTextXMLFace::touchEvent, this)); + button->setLocalZOrder(10); + _widget->addChild(button); + + + // RichText + _richText = RichText::createWithXML("Marker Felt 20.Arial 20.Thonburi 24 blue"); + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + + _richText->setPosition(Vec2(widgetSize.width / 2, widgetSize.height / 2)); + _richText->setLocalZOrder(10); + + + _widget->addChild(_richText); + + // test remove all children, this call won't effect the test + _richText->removeAllChildren(); + + return true; + } + return false; +} + +void UIRichTextXMLFace::touchEvent(Ref *pSender, Widget::TouchEventType type) +{ + switch (type) + { + case Widget::TouchEventType::ENDED: + { + if (_richText->isIgnoreContentAdaptWithSize()) + { + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + } + else + { + _richText->ignoreContentAdaptWithSize(true); + } + } + break; + + default: + break; + } +} + +// +// UIRichTextXMLBR +// +bool UIRichTextXMLBR::init() +{ + if (UIScene::init()) + { + Size widgetSize = _widget->getContentSize(); + + // Add the alert + Text *alert = Text::create("RichText", "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); + + + Button* button = Button::create("cocosui/animationbuttonnormal.png", "cocosui/animationbuttonpressed.png"); + button->setTouchEnabled(true); + button->setTitleText("switch"); + button->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + button->getContentSize().height * 2.5)); + button->addTouchEventListener(CC_CALLBACK_2(UIRichTextXMLBR::touchEvent, this)); + button->setLocalZOrder(10); + _widget->addChild(button); + + + // RichText + _richText = RichText::createWithXML("this is one line.
this should be in another line.
and this is another line"); + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + + _richText->setPosition(Vec2(widgetSize.width / 2, widgetSize.height / 2)); + _richText->setLocalZOrder(10); + + + _widget->addChild(_richText); + + // test remove all children, this call won't effect the test + _richText->removeAllChildren(); + + return true; + } + return false; +} + +void UIRichTextXMLBR::touchEvent(Ref *pSender, Widget::TouchEventType type) +{ + switch (type) + { + case Widget::TouchEventType::ENDED: + { + if (_richText->isIgnoreContentAdaptWithSize()) + { + _richText->ignoreContentAdaptWithSize(false); + _richText->setContentSize(Size(100, 100)); + } + else + { + _richText->ignoreContentAdaptWithSize(true); + } + } + break; + + default: + break; + } +} diff --git a/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/UIRichTextTest/UIRichTextTest.h b/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/UIRichTextTest/UIRichTextTest.h index 078ad87bcc..bca79d5654 100644 --- a/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/UIRichTextTest/UIRichTextTest.h +++ b/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/UIRichTextTest/UIRichTextTest.h @@ -12,14 +12,133 @@ class UIRichTextTest : public UIScene public: CREATE_FUNC(UIRichTextTest); - UIRichTextTest(); - ~UIRichTextTest(); - - virtual bool init() override; + bool init() override; void touchEvent(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type); protected: cocos2d::ui::RichText* _richText; }; +class UIRichTextXMLBasic : public UIScene +{ +public: + CREATE_FUNC(UIRichTextXMLBasic); + + bool init() override; + void touchEvent(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type); + +protected: + cocos2d::ui::RichText* _richText; +}; + +class UIRichTextXMLSmallBig : public UIScene +{ +public: + CREATE_FUNC(UIRichTextXMLSmallBig); + + bool init() override; + void touchEvent(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type); + +protected: + cocos2d::ui::RichText* _richText; +}; + +class UIRichTextXMLColor : public UIScene +{ +public: + CREATE_FUNC(UIRichTextXMLColor); + + bool init() override; + void touchEvent(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type); + +protected: + cocos2d::ui::RichText* _richText; +}; + +class UIRichTextXMLSUIB : public UIScene +{ +public: + CREATE_FUNC(UIRichTextXMLSUIB); + + bool init() override; + void touchEvent(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type); + +protected: + cocos2d::ui::RichText* _richText; +}; + +class UIRichTextXMLSUIB2 : public UIScene +{ +public: + CREATE_FUNC(UIRichTextXMLSUIB2); + + bool init() override; + void touchEvent(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type); + +protected: + cocos2d::ui::RichText* _richText; +}; + +class UIRichTextXMLSUIB3 : public UIScene +{ +public: + CREATE_FUNC(UIRichTextXMLSUIB3); + + bool init() override; + void touchEvent(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type); + +protected: + cocos2d::ui::RichText* _richText; +}; + +class UIRichTextXMLImg : public UIScene +{ +public: + CREATE_FUNC(UIRichTextXMLImg); + + bool init() override; + void touchEvent(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type); + +protected: + cocos2d::ui::RichText* _richText; +}; + +class UIRichTextXMLUrl : public UIScene +{ +public: + CREATE_FUNC(UIRichTextXMLUrl); + + bool init() override; + void touchEvent(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type); + +protected: + cocos2d::ui::RichText* _richText; +}; + +class UIRichTextXMLFace : public UIScene +{ +public: + CREATE_FUNC(UIRichTextXMLFace); + + bool init() override; + void touchEvent(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type); + +protected: + cocos2d::ui::RichText* _richText; +}; + +class UIRichTextXMLBR : public UIScene +{ +public: + CREATE_FUNC(UIRichTextXMLBR); + + bool init() override; + void touchEvent(cocos2d::Ref* sender, cocos2d::ui::Widget::TouchEventType type); + +protected: + cocos2d::ui::RichText* _richText; +}; + + + #endif /* defined(__TestCpp__UIRichTextTest__) */