871 lines
20 KiB
C++
871 lines
20 KiB
C++
#include "dl_text.h"
|
||
|
||
#ifndef DL_DISABLE_RENDER
|
||
#include "math/dl_math.h"
|
||
#include "dl_factory.h"
|
||
#include "io/dl_input.h"
|
||
#include "render/dl_free_type.h"
|
||
#include "render/dl_graphics.h"
|
||
#include "system/dl_system.h"
|
||
|
||
namespace dl
|
||
{
|
||
//! 文本默认order(因为有背景,所以不能为0)
|
||
constexpr float Z_TEXT = Math::OffsetOrder(Z_DEFAULT, 0, 1);
|
||
|
||
Text::Text() :
|
||
_coor{ nullptr },
|
||
_bFormat{ false },
|
||
_ui{ false },
|
||
_fontId{ 0 },
|
||
_areaSize{},
|
||
_order{ Z_TEXT }
|
||
{
|
||
}
|
||
|
||
|
||
|
||
bool Text::SetString(std::string_view str)
|
||
{
|
||
if (_str != str)
|
||
{
|
||
_str = str;
|
||
_refresh_string();
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
void Text::SetFontId(size_t id)
|
||
{
|
||
_fontId = id;
|
||
_refresh_string();
|
||
}
|
||
|
||
|
||
void Text::SetFontSize(unsigned size)
|
||
{
|
||
if (_fontSize[TextParam::NORMAL] == size)
|
||
return;
|
||
_fontSize[TextParam::NORMAL] = size;
|
||
_fontSizeReal[TextParam::NORMAL] = (unsigned)(size / _coor->GetScale()[0]);
|
||
|
||
_refresh_string();
|
||
}
|
||
|
||
|
||
void Text::SetOutline(unsigned outline, const Color& color)
|
||
{
|
||
_fontSize[TextParam::OUTLINE] = outline;
|
||
_fontSizeReal[TextParam::OUTLINE] = (unsigned)(outline / _coor->GetScale()[0]);
|
||
_col[TextParam::OUTLINE] = color;
|
||
|
||
_refresh_string();
|
||
}
|
||
|
||
|
||
void Text::SetShadow(unsigned shadow, const Color& color /*= ColorDef::BLACK*/)
|
||
{
|
||
_fontSize[TextParam::SHADOW] = shadow;
|
||
_fontSizeReal[TextParam::SHADOW] = (unsigned)(shadow / _coor->GetScale()[0]);
|
||
_col[TextParam::SHADOW] = color;
|
||
|
||
_refresh_string();
|
||
}
|
||
|
||
void Text::SetBackground(bool open, const Color& col /*= ColorDef::TEXT_BG*/)
|
||
{
|
||
//此处颜色可能发生改变
|
||
if (open)
|
||
{
|
||
if (!_sprBg)
|
||
{
|
||
_sprBg = g_factory->CreateSpriteDefault(col);
|
||
_sprBg->SetCoor(_coor);
|
||
_sprBg->SetOrder(Math::OffsetOrder(_order, 0, 0, 0));
|
||
}
|
||
else
|
||
_sprBg->SetColor(col);
|
||
_update_spr_bg();
|
||
}
|
||
else
|
||
{
|
||
_sprBg.CancelRef();
|
||
}
|
||
}
|
||
|
||
std::string Text::GetString()
|
||
{
|
||
return _str;
|
||
}
|
||
|
||
Position Text::GetSize()
|
||
{
|
||
return _areaSize * _coor->GetScale();
|
||
}
|
||
|
||
bool Text::IsPickup()
|
||
{
|
||
if (_allNode.size() == 0)
|
||
return false;
|
||
if (_ui && g_factory->GetOrderPickup() < _order)
|
||
return false;
|
||
|
||
Position pos = _coor->WorldToThis(toPosition(g_input->GetCursorPosition()));
|
||
|
||
bool ret = Each::Less({}, pos)
|
||
&& Each::Less(pos, _areaSize);
|
||
return ret;
|
||
}
|
||
|
||
std::size_t Text::Pos2Index(const Position& pos)
|
||
{
|
||
for (size_t i = 0; i < _allNode.size(); ++i)
|
||
{
|
||
TextNode& node = _allNode[i];
|
||
if (!node._spr)
|
||
continue;
|
||
if (Math::IsDotInQuad(pos, node._spr->GetQuad()))
|
||
return i;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
dl::Position Text::Index2Pos(size_t i)
|
||
{
|
||
if (_allNode.empty())
|
||
return {};
|
||
if (i >= _allNode.size())
|
||
{
|
||
TextNode& node = _allNode.back();
|
||
if (node._ch == '\n')
|
||
return { 0, node._xy[1] };
|
||
else
|
||
return _allNode.back().GetRightTop();
|
||
}
|
||
return _allNode[i].GetLeftTop();
|
||
}
|
||
|
||
dl::Position Text::GetNodeSize(size_t i)
|
||
{
|
||
if (i >= _allNode.size())
|
||
return {};
|
||
TextNode& node = _allNode[i];
|
||
return Position{ node._advance, node._lineH } * _coor->GetScale();
|
||
}
|
||
|
||
unsigned Text::GetNodeFontSize(size_t i)
|
||
{
|
||
if (i >= _allNode.size())
|
||
return _fontSize[TextParam::NORMAL];
|
||
TextNode& node = _allNode[i];
|
||
return (unsigned)(node._lineH * _coor->GetScale()[1]);
|
||
}
|
||
|
||
size_t Text::Pos2IndexOutRange(const Position& pos)
|
||
{
|
||
size_t ret = -1;
|
||
// 找到距离最近的,且行距离优先
|
||
float dx_min = std::numeric_limits<float>::max();
|
||
int dy_min = std::numeric_limits<int>::max();// 最近匹配行
|
||
for (size_t i = 0; i < _allNode.size(); ++i)
|
||
{
|
||
TextNode& node = _allNode[i];
|
||
Position lt = node.GetLeftTop();
|
||
int dy = (int)(1 + (lt[1] - pos[1]) / _fontSize[TextParam::NORMAL]);
|
||
if (dy < 0)
|
||
continue;
|
||
if (dy < dy_min)
|
||
{
|
||
dy_min = dy;
|
||
dx_min = std::abs(pos[0] - lt[0]);
|
||
ret = i;
|
||
}
|
||
else if(dy == dy_min)
|
||
{
|
||
Float dx = std::abs(pos[0] - lt[0]);
|
||
if (dx < dx_min)
|
||
{
|
||
dx_min = dx;
|
||
ret = i;
|
||
}
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
size_t Text::Pos2CursorIndex(const Position& pos)
|
||
{
|
||
size_t ret = Pos2Index(pos);
|
||
if (ret == -1)
|
||
{
|
||
ret = Pos2IndexOutRange(pos);
|
||
}
|
||
// 中间偏移
|
||
if (ret != -1 && _allNode[ret]._ch != '\n')
|
||
{
|
||
TextNode& node = _allNode[ret];
|
||
Quad quad = node._spr->GetQuad();
|
||
Position center = quad.Sum(0, 2) / 2_f;
|
||
if (pos[0] > center[0])
|
||
++ret;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
void Text::RenderDebug()
|
||
{
|
||
g_gfx->SetCoor(_coor);
|
||
g_gfx->SetOrder(Math::OffsetOrder(_order, 0, 0, -1));
|
||
g_gfx->SetColor(createColor(ColorDef::GREEN, 0.2f));
|
||
Quad quad = createQuad(_areaSize, false);
|
||
g_gfx->QuadSolid2D(quad);
|
||
g_gfx->SetColor(createColor(ColorDef::RED, 1.0f));
|
||
g_gfx->Line2D(quad.Get(0), quad.Get(2));
|
||
g_gfx->Line2D(quad.Get(1), quad.Get(3));
|
||
}
|
||
|
||
Text::~Text()
|
||
{
|
||
delete _coor;
|
||
}
|
||
|
||
|
||
|
||
void Text::_update_order()
|
||
{
|
||
if (_sprBg)
|
||
_sprBg->SetOrder(Math::OffsetOrder(_order, 0, 0, 0));
|
||
for (auto& iter : _allNode)
|
||
{
|
||
if (iter._spr)
|
||
iter._spr->SetOrder(Math::OffsetOrder(_order, 0, 0, -4));
|
||
if (iter._sprOutline)
|
||
iter._sprOutline->SetOrder(Math::OffsetOrder(_order, 0, 0, -3));
|
||
if (iter._sprShadow)
|
||
iter._sprShadow->SetOrder(Math::OffsetOrder(_order, 0, 0, -2));
|
||
if (iter._sprBg)
|
||
iter._sprBg->SetOrder(Math::OffsetOrder(_order, 0, 0, -1));
|
||
}
|
||
}
|
||
|
||
|
||
void Text::_update_spr_bg()
|
||
{
|
||
_sprBg->SetQuad(createQuad(_areaSize, false));
|
||
}
|
||
|
||
void Text::_refresh_string()
|
||
{
|
||
//由于标签保留使用中文的可能性,这里要保证单个字符比较的正确性
|
||
//而且标签转换数值,所以使用utf_string
|
||
|
||
/*if(p_str_utf)
|
||
_str = CodeCvt::Utf32ToUtf8(*p_str_utf);*/
|
||
|
||
_allNode.clear();
|
||
|
||
|
||
if (_str.empty())
|
||
{
|
||
//这里需要计算出一些数据
|
||
_update_layout();
|
||
return;
|
||
}
|
||
|
||
// 空白字符也生成精灵,用于拾取定位等
|
||
auto fn_write_char = [&](size_t id, unsigned size, unsigned outline, unsigned shadow,
|
||
char32_t ch, const Color& col, const Color& col_outline, const Color& col_shadow,
|
||
const Color& col_bg = ColorDef::NONE)
|
||
{
|
||
auto fn_make_special = [&](TextNode& node, const Size& size) {
|
||
node._quad[0] = createQuad(size, false);
|
||
node._quad[0] += toPosition(0, -(float)size[1]);
|
||
gcSprite spr = g_factory->CreateSpriteDefault();
|
||
spr->SetCoor(_coor);
|
||
spr->SetColor(ColorDef::NONE);
|
||
spr->SetQuad(node._quad[0]);
|
||
node._spr = spr;
|
||
};
|
||
//特殊字符处理
|
||
if (ch == U' ')
|
||
{
|
||
TextNode node;
|
||
node._ch = ch;
|
||
node._lineH = (Float)size;
|
||
node._ascent = (Float)size;
|
||
node._advance = (Float)(size / 2u);
|
||
fn_make_special(node, Size{ size / 2u, size });
|
||
_allNode.push_back(node);
|
||
return;
|
||
}
|
||
if (ch == U'\t')
|
||
{//此处advance为几个字体宽度
|
||
TextNode node;
|
||
node._ch = ch;
|
||
node._lineH = (Float)size;
|
||
node._ascent = (Float)size;
|
||
node._advance = 2;
|
||
fn_make_special(node, Size{ size * 2u, size });
|
||
_allNode.push_back(node);
|
||
return;
|
||
}
|
||
if (ch == U'\n')
|
||
{
|
||
TextNode node;
|
||
node._ch = ch;
|
||
node._lineH = (Float)size;
|
||
node._ascent = (Float)size;
|
||
node._advance = 0;
|
||
fn_make_special(node, Size{ size, size });
|
||
_allNode.push_back(node);
|
||
return;
|
||
}
|
||
|
||
gcSprite spr, spr_outline, spr_shadow;
|
||
Float advance = g_factory->CreateSpriteChar({ id, ch, size, outline }, spr, spr_outline);
|
||
|
||
spr->SetCoor(_coor);
|
||
spr->SetColor(col);
|
||
|
||
|
||
if (outline > 0)
|
||
{
|
||
spr_outline->SetCoor(_coor);
|
||
spr_outline->SetColor(col_outline);
|
||
}
|
||
|
||
if (shadow > 0)
|
||
{
|
||
spr_shadow = outline > 0 ? g_factory->CloneSprite(spr_outline) : g_factory->CloneSprite(spr);
|
||
spr_shadow->SetColor(col_shadow);
|
||
}
|
||
|
||
|
||
const FreeType::Property& prop = g_freetype->GetProperty(id, size);
|
||
TextNode node;
|
||
node._ch = ch;
|
||
node._lineH = prop.GetLinespace();
|
||
node._ascent = prop._ascent;
|
||
node._advance = advance;
|
||
node._shadow = shadow;
|
||
node._spr = spr;
|
||
node._sprOutline = spr_outline;
|
||
node._sprShadow = spr_shadow;
|
||
|
||
gcSprite& spr_bg = node._sprBg;
|
||
if (col_bg != ColorDef::NONE)
|
||
{
|
||
spr_bg = g_factory->CreateSpriteDefault(col_bg);
|
||
spr_bg->SetQuad(createQuad(node.GetLeftTop(), node.GetRightDown()));
|
||
spr_bg->SetCoor(_coor);
|
||
}
|
||
|
||
node.RecordQuad();
|
||
|
||
_allNode.push_back(node);
|
||
};
|
||
|
||
|
||
std::u32string str_utf;
|
||
/*if (p_str_utf)
|
||
str_utf = *p_str_utf;
|
||
else*/
|
||
{
|
||
if (CodeCvt::Utf8::Check(_str.c_str()) == 0)
|
||
{
|
||
log_err0("字符串不是u8编码!");
|
||
|
||
std::string str_u8 = CodeCvt::MultiByteToUtf8(_str.c_str());
|
||
str_utf = CodeCvt::Utf8ToUtf32(str_u8);
|
||
}
|
||
else
|
||
str_utf = CodeCvt::Utf8ToUtf32(_str);
|
||
}
|
||
|
||
|
||
//
|
||
if (_bFormat)
|
||
{
|
||
enum class FlagState
|
||
{
|
||
NORMAL,
|
||
FLAG,
|
||
};
|
||
|
||
|
||
FlagState state = FlagState::NORMAL;//当前读取状态
|
||
Color cur_color = _col[TextParam::NORMAL];//当前颜色
|
||
Color cur_color_outline = _col[TextParam::OUTLINE];//当前颜色
|
||
Color cur_color_shadow = _col[TextParam::SHADOW];//当前颜色
|
||
Color cur_color_bg = ColorDef::NONE;
|
||
|
||
size_t cur_font_id = _fontId;//当前字体
|
||
unsigned cur_font_size = _fontSizeReal[TextParam::NORMAL];
|
||
unsigned cur_font_ouline = _fontSizeReal[TextParam::OUTLINE];//当前描边大小
|
||
unsigned cur_font_shadow = _fontSizeReal[TextParam::SHADOW];//当前阴影偏移
|
||
|
||
Float s = 1.0f / _coor->GetScale()[0];
|
||
|
||
//标签使用wchar_t
|
||
std::wstring str_flag;
|
||
|
||
for (auto& iter : str_utf)
|
||
{
|
||
if (iter == 0)
|
||
{
|
||
if (iter != str_utf.back())
|
||
log_warn0("空字符出现在非尾部!");
|
||
break;
|
||
}
|
||
|
||
const char32_t& ch = iter;
|
||
switch (state)
|
||
{
|
||
case FlagState::NORMAL:
|
||
if (ch == U'[')
|
||
state = FlagState::FLAG;
|
||
else
|
||
{//以当前状态写入一个字符
|
||
fn_write_char(cur_font_id,
|
||
cur_font_size, cur_font_ouline, cur_font_shadow,
|
||
ch, cur_color, cur_color_outline, cur_color_shadow,
|
||
cur_color_bg);
|
||
}
|
||
break;
|
||
case FlagState::FLAG:
|
||
if (ch ==U']')
|
||
{
|
||
//分长度处理
|
||
if (str_flag.empty())
|
||
{
|
||
log_err0("空标签");
|
||
}
|
||
else if (str_flag.size() == 1)
|
||
{
|
||
if (str_flag[0] == L'f')
|
||
{
|
||
cur_font_id = _fontId;
|
||
cur_font_size = _fontSizeReal[TextParam::NORMAL];
|
||
cur_font_ouline = _fontSizeReal[TextParam::OUTLINE];
|
||
cur_font_shadow = _fontSizeReal[TextParam::SHADOW];
|
||
}
|
||
else if (str_flag[0] == L'c')
|
||
{
|
||
cur_color = _col[TextParam::NORMAL];
|
||
cur_color_outline = _col[TextParam::OUTLINE];
|
||
cur_color_shadow = _col[TextParam::SHADOW];
|
||
}
|
||
else if (str_flag[0] == L'b')
|
||
{
|
||
cur_color_bg = ColorDef::NONE;
|
||
}
|
||
else if (str_flag[0] == L'n')
|
||
{
|
||
//以当前状态写入\n
|
||
fn_write_char(cur_font_id, cur_font_size, cur_font_ouline, cur_font_shadow, U'\n', cur_color, cur_color_outline, cur_color_shadow);
|
||
}
|
||
else if (str_flag[0] == L't')
|
||
{
|
||
//以当前状态写入\t
|
||
fn_write_char(cur_font_id, cur_font_size, cur_font_ouline, cur_font_shadow, U'\t', cur_color, cur_color_outline, cur_color_shadow);
|
||
}
|
||
else if (str_flag[0] == L'l')
|
||
{
|
||
//以当前状态写入 [
|
||
fn_write_char(cur_font_id, cur_font_size, cur_font_ouline, cur_font_shadow, U'[', cur_color, cur_color_outline, cur_color_shadow);
|
||
}
|
||
else if (str_flag[0] == L'r')
|
||
{
|
||
//以当前状态写入 ]
|
||
fn_write_char(cur_font_id, cur_font_size, cur_font_ouline, cur_font_shadow, U']', cur_color, cur_color_outline, cur_color_shadow);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (str_flag[1] != L'_')
|
||
log_err0("未知标签:" + CodeCvt::WideCharToUtf8(str_flag));
|
||
else
|
||
{
|
||
if (str_flag[0] == L'f')
|
||
{
|
||
std::vector<std::wstring> vec;//f、id
|
||
split_no_empty(str_flag, L'_', vec);
|
||
|
||
if (vec.size() > 1)
|
||
cur_font_id = stoi(vec[1]);
|
||
}
|
||
if (str_flag[0] == L's')
|
||
{
|
||
std::vector<std::wstring> vec;//s、大小、描边、阴影
|
||
split_no_empty(str_flag, L'_', vec);
|
||
|
||
if (vec.size() > 1)
|
||
cur_font_size = (unsigned)(stoi(vec[1]) * s);
|
||
if (vec.size() > 2)
|
||
cur_font_ouline = (unsigned)(stoi(vec[2]) * s);
|
||
if (vec.size() > 3)
|
||
cur_font_shadow = (unsigned)(stoi(vec[3]) * s);
|
||
}
|
||
else if (str_flag[0] == L'c')
|
||
{
|
||
std::vector<std::wstring> vec;//c、大小、描边、阴影
|
||
split_no_empty(str_flag, L'_', vec);
|
||
|
||
if (vec.size() > 1)
|
||
cur_color = g_factory->GetColor(CodeCvt::WideCharToUtf8(vec[1].c_str()));
|
||
if (vec.size() > 2)
|
||
cur_color_outline = g_factory->GetColor(CodeCvt::WideCharToUtf8(vec[2].c_str()));
|
||
if (vec.size() > 3)
|
||
cur_color_shadow = g_factory->GetColor(CodeCvt::WideCharToUtf8(vec[3].c_str()));
|
||
}
|
||
else if (str_flag[0] == L'n')
|
||
{
|
||
std::vector<std::wstring> vec;//n、次数
|
||
split_no_empty(str_flag, L'_', vec);
|
||
|
||
//以当前状态写入\n 多次
|
||
if (vec.size() > 1)
|
||
{
|
||
size_t i = stoi(vec[1]);
|
||
for (size_t j = 0; j < i; ++j)
|
||
fn_write_char(cur_font_id, cur_font_size,
|
||
cur_font_ouline, cur_font_shadow, U'\n',
|
||
cur_color, cur_color_outline, cur_color_shadow);
|
||
}
|
||
}
|
||
else if (str_flag[0] == L't')
|
||
{
|
||
std::vector<std::wstring> vec;//n、宽度
|
||
split_no_empty(str_flag, L'_', vec);
|
||
|
||
//指定宽度 写入\t
|
||
TextNode node;
|
||
node._ch = U'\t';
|
||
node._advance = (Float)(stoi(vec[1]));
|
||
node._lineH = g_freetype->GetProperty(cur_font_id, cur_font_size).GetLinespace();
|
||
_allNode.push_back(node);
|
||
}
|
||
else if (str_flag[0] == L'b')
|
||
{
|
||
std::vector<std::wstring> vec;//b、背景颜色
|
||
split_no_empty(str_flag, L'_', vec);
|
||
|
||
if (vec.size() > 1)
|
||
cur_color_bg = g_factory->GetColor(CodeCvt::WideCharToUtf8(vec[1].c_str()));
|
||
}
|
||
}
|
||
}
|
||
|
||
state = FlagState::NORMAL;
|
||
str_flag.clear();
|
||
}
|
||
else
|
||
{
|
||
assert(ch < 65536);//TODO utf_char转wchar_t
|
||
str_flag.push_back((wchar_t)ch);
|
||
}
|
||
break;
|
||
default:
|
||
log_err0("错误的标签!");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Float sx = 0;
|
||
for (auto& iter : str_utf)
|
||
{
|
||
if (iter == 0)
|
||
{
|
||
if (iter != str_utf.back())
|
||
log_warn0("空字符出现在非尾部!");
|
||
break;;
|
||
}
|
||
|
||
const char32_t& ch = iter;
|
||
|
||
fn_write_char(_fontId,
|
||
_fontSizeReal[TextParam::NORMAL], _fontSizeReal[TextParam::OUTLINE], _fontSizeReal[TextParam::SHADOW], ch,
|
||
_col[TextParam::NORMAL], _col[TextParam::OUTLINE], _col[TextParam::SHADOW]);
|
||
}
|
||
}
|
||
|
||
_update_order();
|
||
_update_layout();
|
||
}
|
||
|
||
|
||
void Text::_update_layout()
|
||
{
|
||
// 第一遍处理,生成每行
|
||
struct Line
|
||
{
|
||
size_t _beg;// 前闭后开
|
||
size_t _end;
|
||
Float _w;// 宽度
|
||
Float _lineH;// 行间距
|
||
Float _ascent;// 基线上方高度
|
||
};
|
||
std::vector<Line> vec_line;
|
||
size_t j = 0;//单行下标
|
||
size_t beg = 0;
|
||
Float sx = 0;
|
||
Float max_h = (Float)_fontSizeReal[TextParam::NORMAL];
|
||
Float max_base = 0;
|
||
for (size_t cur = 0; cur < _allNode.size(); ++cur)
|
||
{
|
||
auto& node = _allNode[cur];
|
||
// update_layout会多次调用,注意不能随便改变node的内容
|
||
if (node._ch == '\n')
|
||
{
|
||
// 换行符在当前行
|
||
vec_line.push_back({ beg, cur + 1, sx, max_h, max_base });
|
||
beg = cur + 1;
|
||
sx = 0;
|
||
max_h = (Float)_fontSizeReal[TextParam::NORMAL];
|
||
max_base = 0;
|
||
}
|
||
else if (node._ch == '\t')
|
||
{
|
||
unsigned advance = (unsigned)(_fontSizeReal[TextParam::NORMAL] * node._advance);
|
||
Float x = (Float)Math::Align((unsigned)sx, advance);
|
||
//对齐后任在原地,则步进advance
|
||
if (x == sx)
|
||
{
|
||
node._advance1 = (Float)advance;
|
||
sx += advance;
|
||
}
|
||
else
|
||
{
|
||
node._advance1 = x - sx;
|
||
sx = x;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (_layout._maxW)
|
||
{//自动换行
|
||
Float s = 1.0f / _coor->GetScale()[0];
|
||
Float max_w = _layout._maxW * s;
|
||
if (sx + node._advance * 2.0f > max_w)
|
||
{
|
||
//如果开启行首不能为特定符号
|
||
if (_layout._isCantFirstPunct
|
||
&& j != 0//不是首行
|
||
)
|
||
//TODO:由于编码问题,这里暂时未处理
|
||
//&& !_is_char_can_first(node._ch))//不能在行首
|
||
{
|
||
sx += node._advance;
|
||
if (node._lineH > max_h)
|
||
max_h = node._lineH;
|
||
}
|
||
else
|
||
{
|
||
//如果开启单词必须在单行
|
||
if (_layout._isWordInSingleLine
|
||
&& isalpha(node._ch))
|
||
{//往前回退到非字母
|
||
for (size_t i = cur; i > beg; --i)
|
||
{
|
||
if (!isalpha(_allNode[i]._ch))
|
||
{
|
||
vec_line.push_back({ beg, i, sx, max_h, max_base });
|
||
beg = i;
|
||
sx = 0;
|
||
max_h = (Float)_fontSizeReal[TextParam::NORMAL];
|
||
max_base = 0;
|
||
break;
|
||
}
|
||
else//每回退一个减去对应宽度
|
||
sx -= _allNode[i]._advance;
|
||
}
|
||
}
|
||
else
|
||
{//正常自动换行
|
||
sx += node._advance;
|
||
// 下次循环cur会加1
|
||
vec_line.push_back({ beg, cur + 1, sx, max_h, max_base });
|
||
beg = cur + 1;
|
||
sx = 0;
|
||
max_h = (Float)_fontSizeReal[TextParam::NORMAL];
|
||
max_base = 0;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
sx += node._advance;
|
||
if (node._ascent > max_base)
|
||
max_base = node._ascent;
|
||
|
||
if (node._lineH > max_h)
|
||
max_h = node._lineH;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
sx += node._advance;
|
||
if (node._ascent > max_base)
|
||
max_base = node._ascent;
|
||
|
||
if (node._lineH > max_h)
|
||
max_h = node._lineH;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (beg != _allNode.size())
|
||
{//尾行
|
||
vec_line.push_back({ beg, _allNode.size(), sx, max_h, max_base });
|
||
}
|
||
|
||
|
||
|
||
//计算max_w
|
||
Float max_w = 0;
|
||
for (auto& iter : vec_line)
|
||
{
|
||
if (iter._w > max_w)
|
||
max_w = iter._w;
|
||
//dnd_debug(DL::TEST, format("[{}, {}) - {:.0f},{:.0f}", iter._beg, iter._end, iter._w, iter._h));
|
||
}
|
||
|
||
//逐行设定位置
|
||
Float sy = 0;
|
||
for (auto& iter : vec_line)
|
||
{
|
||
Float x = 0;
|
||
if (_layout._alignHorizontalLine == Align::Direction::FRONT)
|
||
x = max_w - iter._w;
|
||
else if (_layout._alignHorizontalLine == Align::Direction::ZERO)
|
||
x = (max_w - iter._w) / 2;
|
||
|
||
|
||
Float sx = 0;
|
||
for (size_t i = iter._beg; i < iter._end; ++i)
|
||
{
|
||
auto& node = _allNode[i];
|
||
|
||
//垂直对齐,本行内
|
||
//底对齐,往下偏移本行最大基线大小
|
||
Float y = node._ascent;
|
||
|
||
node.Set(sx + x, sy + y);
|
||
if (node._ch == '\t')
|
||
sx += node._advance1;
|
||
else
|
||
sx += node._advance;
|
||
}
|
||
sy += (iter._lineH * _layout._lineSpacing);
|
||
}
|
||
|
||
// sy为实际整体高度
|
||
_areaSize = { max_w, sy };
|
||
|
||
if (_sprBg)
|
||
_update_spr_bg();
|
||
}
|
||
|
||
bool Text::_is_char_can_first(char ch)
|
||
{
|
||
// && ch != ','
|
||
// && ch != '。'
|
||
// && ch != '!'
|
||
// && ch != '?'
|
||
return !ispunct(ch)
|
||
|| (ch != ','
|
||
&& ch != '.'
|
||
&& ch != '!'
|
||
&& ch != '?');
|
||
}
|
||
|
||
bool Text::_update_scale()
|
||
{
|
||
// 计算缩放
|
||
Float s = 1.0f;
|
||
Coor* coor = _coor->GetParent();
|
||
while (coor)
|
||
{
|
||
s *= coor->GetScale()[0];
|
||
coor = coor->GetParent();
|
||
}
|
||
assert(s);
|
||
|
||
Float s_cur = _coor->GetScale()[0];
|
||
Float s_inv = 1.0f / s;
|
||
|
||
if (std::abs(s_inv - s_cur) > 0.01f)
|
||
{
|
||
_fontSizeReal[TextParam::NORMAL] = (unsigned)(_fontSize[TextParam::NORMAL] * s);
|
||
_fontSizeReal[TextParam::OUTLINE] = (unsigned)(_fontSize[TextParam::OUTLINE] * s);
|
||
_fontSizeReal[TextParam::SHADOW] = (unsigned)(_fontSize[TextParam::SHADOW] * s);
|
||
_coor->SetScale(s_inv);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void Text::Render()
|
||
{
|
||
if (_update_scale())
|
||
{
|
||
_refresh_string();
|
||
}
|
||
|
||
if (_ui && IsPickup())
|
||
{
|
||
if (g_system->GetStep() != SystemStep::RENDER)
|
||
{
|
||
log_err0("请在Render函数调用,否则无法正确处理拾取穿透!");
|
||
}
|
||
g_factory->SetOrderPickup(_order);
|
||
g_factory->_add_pickup_text(this);
|
||
}
|
||
|
||
if (_sprBg)
|
||
_sprBg->Render();
|
||
for (auto& iter : _allNode)
|
||
{
|
||
if (iter._sprShadow)
|
||
iter._sprShadow->Render();
|
||
if (iter._sprOutline)
|
||
iter._sprOutline->Render();
|
||
if (iter._spr)
|
||
iter._spr->Render();
|
||
if (iter._sprBg)
|
||
iter._sprBg->Render();
|
||
}
|
||
}
|
||
|
||
|
||
void Text::SetCenter(const Position& pos)
|
||
{
|
||
SetPositionAlign(pos, { Align::Direction::ZERO, Align::Direction::ZERO });
|
||
}
|
||
|
||
void Text::SetOrder(float order)
|
||
{
|
||
if (_order != order)
|
||
{
|
||
_order = order;
|
||
_update_order();
|
||
}
|
||
}
|
||
|
||
|
||
void Text::SetColor(const Color& col, TextParam type)
|
||
{
|
||
if (_col[type] != col)
|
||
{
|
||
_col[type] = col;
|
||
_refresh_string();
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
#endif |