446 lines
7.9 KiB
C++
446 lines
7.9 KiB
C++
#include "..\e2dnode.h"
|
||
|
||
//-------------------------------------------------------
|
||
// Style
|
||
//-------------------------------------------------------
|
||
|
||
e2d::Text::Style::Style()
|
||
: color(Color::White)
|
||
, alignment(Align::Left)
|
||
, wrapping(false)
|
||
, wrappingWidth(0.f)
|
||
, lineSpacing(0.f)
|
||
, hasUnderline(false)
|
||
, hasStrikethrough(false)
|
||
, hasOutline(true)
|
||
, outlineColor(Color(Color::Black, 0.5))
|
||
, outlineWidth(1.f)
|
||
, outlineJoin(LineJoin::Round)
|
||
{}
|
||
|
||
e2d::Text::Style::Style(
|
||
Color color,
|
||
Align alignment,
|
||
bool wrapping,
|
||
float wrappingWidth,
|
||
float lineSpacing,
|
||
bool hasUnderline,
|
||
bool hasStrikethrough,
|
||
bool hasOutline,
|
||
Color outlineColor,
|
||
float outlineWidth,
|
||
LineJoin outlineJoin
|
||
)
|
||
: color(color)
|
||
, alignment(alignment)
|
||
, wrapping(wrapping)
|
||
, wrappingWidth(wrappingWidth)
|
||
, lineSpacing(lineSpacing)
|
||
, hasUnderline(hasUnderline)
|
||
, hasStrikethrough(hasStrikethrough)
|
||
, hasOutline(hasOutline)
|
||
, outlineColor(outlineColor)
|
||
, outlineWidth(outlineWidth)
|
||
, outlineJoin(outlineJoin)
|
||
{}
|
||
|
||
|
||
|
||
//-------------------------------------------------------
|
||
// Text
|
||
//-------------------------------------------------------
|
||
|
||
e2d::Text::Text()
|
||
: _font()
|
||
, _style()
|
||
, _textLayout(nullptr)
|
||
, _textFormat(nullptr)
|
||
{
|
||
}
|
||
|
||
e2d::Text::Text(const String & text, const Font & font, const Style & style)
|
||
: _font(font)
|
||
, _style(style)
|
||
, _textLayout(nullptr)
|
||
, _textFormat(nullptr)
|
||
, _text(text)
|
||
{
|
||
_reset();
|
||
}
|
||
|
||
e2d::Text::~Text()
|
||
{
|
||
SafeRelease(_textFormat);
|
||
SafeRelease(_textLayout);
|
||
}
|
||
|
||
e2d::String e2d::Text::getText() const
|
||
{
|
||
return _text;
|
||
}
|
||
|
||
e2d::Font e2d::Text::getFont() const
|
||
{
|
||
return _font;
|
||
}
|
||
|
||
e2d::Text::Style e2d::Text::getStyle() const
|
||
{
|
||
return _style;
|
||
}
|
||
|
||
e2d::String e2d::Text::getFontFamily() const
|
||
{
|
||
return _font.family;
|
||
}
|
||
|
||
float e2d::Text::getFontSize() const
|
||
{
|
||
return _font.size;
|
||
}
|
||
|
||
UINT e2d::Text::getFontWeight() const
|
||
{
|
||
return _font.weight;
|
||
}
|
||
|
||
e2d::Color e2d::Text::getColor() const
|
||
{
|
||
return _style.color;
|
||
}
|
||
|
||
e2d::Color e2d::Text::getOutlineColor() const
|
||
{
|
||
return _style.outlineColor;
|
||
}
|
||
|
||
float e2d::Text::getOutlineWidth() const
|
||
{
|
||
return _style.outlineWidth;
|
||
}
|
||
|
||
e2d::LineJoin e2d::Text::getOutlineJoin() const
|
||
{
|
||
return _style.outlineJoin;
|
||
}
|
||
|
||
int e2d::Text::getLineCount() const
|
||
{
|
||
if (_textLayout)
|
||
{
|
||
DWRITE_TEXT_METRICS metrics;
|
||
_textLayout->GetMetrics(&metrics);
|
||
return static_cast<int>(metrics.lineCount);
|
||
}
|
||
else
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
bool e2d::Text::isItalic() const
|
||
{
|
||
return _font.italic;
|
||
}
|
||
|
||
bool e2d::Text::hasStrikethrough() const
|
||
{
|
||
return _style.hasStrikethrough;
|
||
}
|
||
|
||
bool e2d::Text::hasUnderline() const
|
||
{
|
||
return _style.hasUnderline;
|
||
}
|
||
|
||
bool e2d::Text::hasOutline() const
|
||
{
|
||
return _style.hasOutline;
|
||
}
|
||
|
||
void e2d::Text::setText(const String& text)
|
||
{
|
||
_text = text;
|
||
_reset();
|
||
}
|
||
|
||
void e2d::Text::setStyle(const Style& style)
|
||
{
|
||
_style = style;
|
||
_reset();
|
||
}
|
||
|
||
void e2d::Text::setFont(const Font & font)
|
||
{
|
||
_font = font;
|
||
_reset();
|
||
}
|
||
|
||
void e2d::Text::setFontFamily(const String& family)
|
||
{
|
||
_font.family = family;
|
||
_reset();
|
||
}
|
||
|
||
void e2d::Text::setFontSize(float size)
|
||
{
|
||
_font.size = size;
|
||
_reset();
|
||
}
|
||
|
||
void e2d::Text::setFontWeight(UINT weight)
|
||
{
|
||
_font.weight = weight;
|
||
_reset();
|
||
}
|
||
|
||
void e2d::Text::setColor(Color color)
|
||
{
|
||
_style.color = color;
|
||
}
|
||
|
||
void e2d::Text::setItalic(bool value)
|
||
{
|
||
_font.italic = value;
|
||
_reset();
|
||
}
|
||
|
||
void e2d::Text::setWrapping(bool wrapping)
|
||
{
|
||
if (_style.wrapping != wrapping)
|
||
{
|
||
_style.wrapping = wrapping;
|
||
_reset();
|
||
}
|
||
}
|
||
|
||
void e2d::Text::setWrappingWidth(float wrappingWidth)
|
||
{
|
||
if (_style.wrappingWidth != wrappingWidth)
|
||
{
|
||
_style.wrappingWidth = std::max(wrappingWidth, 0.f);
|
||
|
||
if (_style.wrapping)
|
||
{
|
||
_reset();
|
||
}
|
||
}
|
||
}
|
||
|
||
void e2d::Text::setLineSpacing(float lineSpacing)
|
||
{
|
||
if (_style.lineSpacing != lineSpacing)
|
||
{
|
||
_style.lineSpacing = lineSpacing;
|
||
_reset();
|
||
}
|
||
}
|
||
|
||
void e2d::Text::setAlignment(Align align)
|
||
{
|
||
if (_style.alignment != align)
|
||
{
|
||
_style.alignment = align;
|
||
_reset();
|
||
}
|
||
}
|
||
|
||
void e2d::Text::setUnderline(bool hasUnderline)
|
||
{
|
||
if (_style.hasUnderline != hasUnderline)
|
||
{
|
||
_style.hasUnderline = hasUnderline;
|
||
if (!_textFormat)
|
||
_createFormat();
|
||
_createLayout();
|
||
}
|
||
}
|
||
|
||
void e2d::Text::setStrikethrough(bool hasStrikethrough)
|
||
{
|
||
if (_style.hasStrikethrough != hasStrikethrough)
|
||
{
|
||
_style.hasStrikethrough = hasStrikethrough;
|
||
if (!_textFormat)
|
||
_createFormat();
|
||
_createLayout();
|
||
}
|
||
}
|
||
|
||
void e2d::Text::setOutline(bool hasOutline)
|
||
{
|
||
_style.hasOutline = hasOutline;
|
||
}
|
||
|
||
void e2d::Text::setOutlineColor(Color outlineColor)
|
||
{
|
||
_style.outlineColor = outlineColor;
|
||
}
|
||
|
||
void e2d::Text::setOutlineWidth(float outlineWidth)
|
||
{
|
||
_style.outlineWidth = outlineWidth;
|
||
}
|
||
|
||
void e2d::Text::setOutlineJoin(LineJoin outlineJoin)
|
||
{
|
||
_style.outlineJoin = outlineJoin;
|
||
}
|
||
|
||
void e2d::Text::onRender() const
|
||
{
|
||
if (_textLayout)
|
||
{
|
||
auto renderer = Renderer::getInstance();
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD><C4B1><EFBFBD><EFBFBD><EFBFBD>
|
||
D2D1_RECT_F textLayoutRect = D2D1::RectF(0, 0, _width, _height);
|
||
// <20><><EFBFBD>û<EFBFBD>ˢ<EFBFBD><CBA2>ɫ<EFBFBD><C9AB><EFBFBD><CDB8><EFBFBD><EFBFBD>
|
||
renderer->getSolidColorBrush()->SetOpacity(_displayOpacity);
|
||
// <20><>ȡ<EFBFBD>ı<EFBFBD><C4B1><EFBFBD>Ⱦ<EFBFBD><C8BE>
|
||
auto pTextRenderer = renderer->getTextRenderer();
|
||
pTextRenderer->SetTextStyle(
|
||
_style.color.toD2DColorF(),
|
||
_style.hasOutline,
|
||
_style.outlineColor.toD2DColorF(),
|
||
_style.outlineWidth,
|
||
D2D1_LINE_JOIN(_style.outlineJoin)
|
||
);
|
||
_textLayout->Draw(nullptr, pTextRenderer, 0, 0);
|
||
}
|
||
}
|
||
|
||
void e2d::Text::_reset()
|
||
{
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD>ʽ<EFBFBD><CABD>
|
||
_createFormat();
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֲ<EFBFBD><D6B2><EFBFBD>
|
||
_createLayout();
|
||
}
|
||
|
||
void e2d::Text::_createFormat()
|
||
{
|
||
SafeRelease(_textFormat);
|
||
|
||
HRESULT hr = Renderer::getWriteFactory()->CreateTextFormat(
|
||
(const WCHAR *)_font.family,
|
||
nullptr,
|
||
DWRITE_FONT_WEIGHT(_font.weight),
|
||
_font.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL,
|
||
DWRITE_FONT_STRETCH_NORMAL,
|
||
_font.size,
|
||
L"",
|
||
&_textFormat
|
||
);
|
||
|
||
if (FAILED(hr))
|
||
{
|
||
WARN("Text::_createFormat error : Create IDWriteTextFormat failed!");
|
||
_textFormat = nullptr;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֶ<EFBFBD><D6B6>뷽ʽ
|
||
_textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT(_style.alignment));
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD>м<EFBFBD><D0BC><EFBFBD>
|
||
if (_style.lineSpacing == 0.f)
|
||
{
|
||
_textFormat->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_DEFAULT, 0, 0);
|
||
}
|
||
else
|
||
{
|
||
_textFormat->SetLineSpacing(
|
||
DWRITE_LINE_SPACING_METHOD_UNIFORM,
|
||
_style.lineSpacing,
|
||
_style.lineSpacing * 0.8f
|
||
);
|
||
}
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD><C4B1>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
if (_style.wrapping)
|
||
{
|
||
_textFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
|
||
}
|
||
else
|
||
{
|
||
_textFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
|
||
}
|
||
}
|
||
}
|
||
|
||
void e2d::Text::_createLayout()
|
||
{
|
||
SafeRelease(_textLayout);
|
||
|
||
// <20>ı<EFBFBD>Ϊ<EFBFBD><CEAA><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
if (_text.isEmpty())
|
||
{
|
||
this->setSize(0, 0);
|
||
return;
|
||
}
|
||
|
||
if (_textFormat == nullptr)
|
||
{
|
||
WARN("Text::_createLayout failed! _textFormat NULL pointer exception.");
|
||
return;
|
||
}
|
||
|
||
UINT32 length = (UINT32)_text.getLength();
|
||
|
||
// <20><><EFBFBD><EFBFBD> TextLayout
|
||
HRESULT hr;
|
||
// <20><><EFBFBD>ı<EFBFBD><C4B1>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>½<EFBFBD><C2BD>д<EFBFBD><D0B4><EFBFBD>
|
||
if (_style.wrapping)
|
||
{
|
||
hr = Renderer::getWriteFactory()->CreateTextLayout(
|
||
(const WCHAR *)_text,
|
||
length,
|
||
_textFormat,
|
||
_style.wrappingWidth,
|
||
0,
|
||
&_textLayout
|
||
);
|
||
if (_textLayout)
|
||
{
|
||
// <20><>ȡ<EFBFBD>ı<EFBFBD><C4B1><EFBFBD><EFBFBD>ֵĿ<D6B5><C4BF>Ⱥ߶<CDB8>
|
||
DWRITE_TEXT_METRICS metrics;
|
||
_textLayout->GetMetrics(&metrics);
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD><C4B1><EFBFBD><EFBFBD><EFBFBD>
|
||
this->setSize(metrics.layoutWidth, metrics.height);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
hr = Renderer::getWriteFactory()->CreateTextLayout((const WCHAR *)_text, length, _textFormat, 0, 0, &_textLayout);
|
||
// Ϊ<><CEAA>ֹ<EFBFBD>ı<EFBFBD><C4B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>⣬<EFBFBD><E2A3AC><EFBFBD>ݸղŴ<D5B2><C5B4><EFBFBD><EFBFBD><EFBFBD> layout <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>´<EFBFBD><C2B4><EFBFBD><EFBFBD><EFBFBD>
|
||
if (_textLayout)
|
||
{
|
||
// <20><>ȡ<EFBFBD>ı<EFBFBD><C4B1><EFBFBD><EFBFBD>ֵĿ<D6B5><C4BF>Ⱥ߶<CDB8>
|
||
DWRITE_TEXT_METRICS metrics;
|
||
_textLayout->GetMetrics(&metrics);
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD><C4B1><EFBFBD><EFBFBD><EFBFBD>
|
||
this->setSize(metrics.width, metrics.height);
|
||
// <20><><EFBFBD>´<EFBFBD><C2B4><EFBFBD> layout
|
||
SafeRelease(_textLayout);
|
||
hr = Renderer::getWriteFactory()->CreateTextLayout((const WCHAR *)_text, length, _textFormat, _width, 0, &_textLayout);
|
||
}
|
||
}
|
||
|
||
if (FAILED(hr))
|
||
{
|
||
WARN("Text::_createLayout error : Create IDWriteTextLayout failed!");
|
||
_textLayout = nullptr;
|
||
return;
|
||
}
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD>»<EFBFBD><C2BB>ߺ<EFBFBD>ɾ<EFBFBD><C9BE><EFBFBD><EFBFBD>
|
||
DWRITE_TEXT_RANGE range = { 0, length };
|
||
if (_style.hasUnderline)
|
||
{
|
||
_textLayout->SetUnderline(true, range);
|
||
}
|
||
if (_style.hasStrikethrough)
|
||
{
|
||
_textLayout->SetStrikethrough(true, range);
|
||
}
|
||
}
|