//
// RichLabel.cpp
// DragonCrownOrigin
//
// Created by txg on 15/1/9.
// 格式 [i:图片路径|-] [s:22|c:00ff00ff]***[-] [s:22]***[-] [l:item/1001]...[-] 随意组合嵌套
//
#include "RichLabel.h"
#include "2d/CCFontFreeType.h"
//#include "FontTextureCache.h"
#include "renderer/CCTextureAtlas.h"
#define DEF_COLOR #ffffff
#define DEF_SIZE 12
#define DEF_FONT_NAME "fonts/yahei.ttf"
#define DEF_TEXTURE_WIDTH 512
#define DEF_TEXTURE_HEIGHT 512
#define DEF_LINK_PRESS_COLOR Color4B(75, 13, 139, 255)
USING_NS_CC;
RichLabel* RichLabel::create(const std::string& text, int width, const std::function<void(const cocos2d::Vec2&, const std::string&)>& linkCallback)
{
static RichLabel* _instance = nullptr;
if (_instance == nullptr)
{
_instance = new RichLabel(text, width, linkCallback);
}
return _instance;
}
const std::string& RichLabel::getText()
{
return _text;
}
void RichLabel::setText(const std::string& text)
{
reset();
_text = text;
analysisText();
updateQuads();
}
int RichLabel::getWidth()
{
return _maxWidth;
}
void RichLabel::setWidth(int width)
{
_maxWidth = width;
updateQuads();
}
void RichLabel::setLinkCallback(const std::function<void(const cocos2d::Vec2&, const std::string&)>& linkCallback)
{
_linkCallback = linkCallback;
}
void RichLabel::draw(cocos2d::Renderer *renderer, const cocos2d::Mat4& transform, uint32_t flags)
{
bool transformUpdated = flags & FLAGS_TRANSFORM_DIRTY;
_insideBounds = transformUpdated ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
if (_insideBounds)
{
_customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(RichLabel::onDraw, this, transform, transformUpdated);
renderer->addCommand(&_customCommand);
}
}
const cocos2d::BlendFunc& RichLabel::getBlendFunc() const
{
return _blendFunc;
}
void RichLabel::setBlendFunc(const cocos2d::BlendFunc &blendFunc)
{
_blendFunc = blendFunc;
}
void RichLabel::onEnter()
{
Node::onEnter();
}
void RichLabel::onDraw(const cocos2d::Mat4& transform, bool transformUpdated)
{
setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_LABEL_NORMAL));
auto glprogram = getGLProgram();
glprogram->use();
GL::blendFunc(_blendFunc.src, _blendFunc.dst);
glprogram->setUniformsForBuiltins(transform);
for (auto atlas :_texAtlass) {
atlas->drawQuads();
}
}
bool RichLabel::onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* e)
{
return true;
}
void RichLabel::onTouchMoved(cocos2d::Touch* touch, cocos2d::Event* e)
{
Vec2 point = this->convertToNodeSpace(touch->getLocation());
Rect r = Rect(0, 0, _contentSize.width, _contentSize.height);
_isPressLink = false;
if (r.containsPoint(point))
{
for (auto pair :_letterLinkIndexs)
{
const LetterInfo& info = _letterInfos[pair];
if (info.rect.containsPoint(point))
{
_isPressLink = true;
if (_currPressInfoIndex != -1)
{
const LetterInfo& oldinfo = _letterInfos[_currPressInfoIndex];
setQuads(oldinfo.linkIndex, oldinfo.color);
}
_currPressInfoIndex = pair;
setQuads(info.linkIndex, DEF_LINK_PRESS_COLOR);
break;
}
}
}
if (!_isPressLink && _currPressInfoIndex != -1)
{
const LetterInfo& info = _letterInfos[_currPressInfoIndex];
setQuads(info.linkIndex, info.color);
_currPressInfoIndex = -1;
}
}
void RichLabel::onTouchEnded(cocos2d::Touch* touch, cocos2d::Event* e)
{
Vec2 point = this->convertToNodeSpace(touch->getLocation());
Rect r = Rect(0, 0, _contentSize.width, _contentSize.height);
_isPressLink = false;
if (r.containsPoint(point))
{
for (auto pair :_letterLinkIndexs)
{
LetterInfo& info = _letterInfos[pair];
if (info.rect.containsPoint(point))
{
_isPressLink = true;
_currPressInfoIndex = pair;
setLetterColor(info.linkIndex, DEF_LINK_PRESS_COLOR);
setQuads(info.linkIndex, DEF_LINK_PRESS_COLOR);
if (_linkCallback)
{
_linkCallback(point, _links[info.linkIndex]);
}
break;
}
}
}
if (!_isPressLink && _currPressInfoIndex != -1)
{
const LetterInfo& info = _letterInfos[_currPressInfoIndex];
setQuads(info.linkIndex, info.color);
_currPressInfoIndex = -1;
}
}
void RichLabel::onTouchCancelled(cocos2d::Touch* touch, cocos2d::Event* e)
{
for (auto pair :_letterLinkIndexs)
{
const LetterInfo& info = _letterInfos[pair];
if (info.type == FormatType::TEXT)
{
int texAtlassIndex = _fontLetterDefinitions[info.targetIndex].pageDataId;
cocos2d::V3F_C4B_T2F_Quad *quad = &_texAtlass[texAtlassIndex]->getQuads()[info.quadIndex];
if (quad->tr.colors != info.color)
{
quad->tr.colors = info.color;
quad->tl.colors = info.color;
quad->br.colors = info.color;
quad->bl.colors = info.color;
_texAtlass[texAtlassIndex]->updateQuad(quad, info.quadIndex);
}
}
}
}
void RichLabel::analysisText()
{
std::u16string utf16String;
StringUtils::UTF8ToUTF16(_text, utf16String);
int index = 0;
std::u16string str ;
while (index < utf16String.length())
{
if (utf16String[index] == '[')
{
if (str.length() > 0)
{
int size = convertStringToInt(lastFindFormat('s', _formatInfo)->value);
const Color4B& color = convertStringToColor4B(lastFindFormat('c', _formatInfo)->value);
appendText(str, lastFindFormat('l', _formatInfo) ? _links.size()-1 : -1, size, color);
str.clear();
}
}
else if(utf16String[index] == ']')
{
if (str[0] == '-')
{
FormatInfo* n = _formatInfo->parent;
if (n)
{
n->removeAllChild();
_formatInfo = n;
}
else
{
delete _formatInfo;
_formatInfo = nullptr;
}
}
else
{
std::string utf8String;
StringUtils::UTF16ToUTF8(str, utf8String);
analysisFormatInfo(utf8String);
if (_formatInfo->key == 'i')
{
appendImage(_formatInfo->value, lastFindFormat('l', _formatInfo) ? _links.size()-1 : -1);
FormatInfo* n = _formatInfo->parent;
n->removeAllChild();
_formatInfo = n;
}
}
str.clear();
}
else
{
str += utf16String[index];
}
index ++;
}
}
void RichLabel::appendImage(const std::string& path, int linkIndex)
{
_imagePaths.push_back(path);
LetterInfo&& letterInfo = LetterInfo(FormatType::IMAGE, _imagePaths.size()-1, linkIndex);
_letterInfos.push_back(std::move(letterInfo));
CCLOG("image path %s, linkIndex %d", path.c_str(), linkIndex);
}
void RichLabel::appendText(std::u16string& text, int linkIndex, int size, const cocos2d::Color4B& color)
{
FontFreeType* fontFreeType = FontFreeType::create(DEF_FONT_NAME, size, GlyphCollection::DYNAMIC, nullptr);
fontFreeType->autorelease();
long width = 0, height = 0;
int xAdvance = 0;
Rect rect;
int length = (int)text.length();
LetterDefinition letterDef;
int startY = (int)_currPageHeight;
CCLO