Update TextLayout
This commit is contained in:
parent
2c050cafba
commit
1cb97ba0b2
|
|
@ -223,21 +223,22 @@ void Canvas::DrawTexture(TexturePtr texture, const Rect* src_rect, const Rect* d
|
|||
}
|
||||
}
|
||||
|
||||
void Canvas::DrawTextLayout(String const& text, Point const& point)
|
||||
void Canvas::DrawTextLayout(String const& text, TextStyle const& style, Point const& point)
|
||||
{
|
||||
if (text.empty())
|
||||
return;
|
||||
|
||||
TextLayout layout;
|
||||
layout.SetStyle(text_style_);
|
||||
layout.SetText(text);
|
||||
DrawTextLayout(layout, point);
|
||||
DrawTextLayout(TextLayout::Create(text, style), point);
|
||||
}
|
||||
|
||||
void Canvas::DrawTextLayout(TextLayout const& layout, Point const& point)
|
||||
void Canvas::DrawTextLayout(TextLayoutPtr layout, Point const& point)
|
||||
{
|
||||
KGE_ASSERT(ctx_);
|
||||
ctx_->DrawTextLayout(layout, point);
|
||||
if (layout)
|
||||
{
|
||||
ctx_->DrawTextLayout(*layout, point);
|
||||
cache_expired_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::BeginPath(Point const& begin_pos)
|
||||
|
|
|
|||
|
|
@ -126,14 +126,15 @@ public:
|
|||
/// \~chinese
|
||||
/// @brief 绘制文字布局
|
||||
/// @param text 文字
|
||||
/// @param style 文字样式
|
||||
/// @param point 绘制文字的位置
|
||||
void DrawTextLayout(String const& text, Point const& point);
|
||||
void DrawTextLayout(String const& text, TextStyle const& style, Point const& point);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 绘制文字布局
|
||||
/// @param layout 文字布局
|
||||
/// @param point 绘制布局的位置
|
||||
void DrawTextLayout(TextLayout const& layout, Point const& point);
|
||||
void DrawTextLayout(TextLayoutPtr layout, Point const& point);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 开始绘制路径
|
||||
|
|
@ -218,11 +219,6 @@ public:
|
|||
/// @param stroke_style 轮廓样式
|
||||
void SetStrokeStyle(StrokeStylePtr stroke_style);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文字画刷样式
|
||||
/// @param text_style 文字画刷样式
|
||||
void SetTextStyle(TextStyle const& text_style);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置画刷
|
||||
/// @param[in] brush 画刷
|
||||
|
|
@ -285,7 +281,6 @@ private:
|
|||
|
||||
private:
|
||||
float stroke_width_;
|
||||
TextStyle text_style_;
|
||||
StrokeStylePtr stroke_style_;
|
||||
ShapeSink shape_sink_;
|
||||
BrushPtr fill_brush_;
|
||||
|
|
@ -313,11 +308,6 @@ inline void Canvas::SetStrokeStyle(StrokeStylePtr stroke_style)
|
|||
stroke_style_ = stroke_style;
|
||||
}
|
||||
|
||||
inline void Canvas::SetTextStyle(TextStyle const& text_style)
|
||||
{
|
||||
text_style_ = text_style;
|
||||
}
|
||||
|
||||
inline void Canvas::SetStrokeColor(Color const& color)
|
||||
{
|
||||
if (!stroke_brush_)
|
||||
|
|
|
|||
|
|
@ -59,13 +59,11 @@ DebugActor::DebugActor()
|
|||
BrushPtr fill_brush = new Brush;
|
||||
fill_brush->SetColor(Color::White);
|
||||
|
||||
TextStyle style;
|
||||
style.font_family = "Arial";
|
||||
style.font_size = 16.f;
|
||||
style.font_weight = FontWeight::Normal;
|
||||
style.line_spacing = 20.f;
|
||||
style.fill_brush = fill_brush;
|
||||
debug_text_.SetStyle(style);
|
||||
debug_text_style_.font_family = "Arial";
|
||||
debug_text_style_.font_size = 16.f;
|
||||
debug_text_style_.font_weight = FontWeight::Normal;
|
||||
debug_text_style_.line_spacing = 20.f;
|
||||
debug_text_style_.fill_brush = fill_brush;
|
||||
|
||||
AddListener<MouseHoverEvent>([=](Event*) { SetOpacity(0.4f); });
|
||||
AddListener<MouseOutEvent>([=](Event*) { SetOpacity(1.f); });
|
||||
|
|
@ -124,8 +122,7 @@ void DebugActor::OnUpdate(Duration dt)
|
|||
ss << pmc.PrivateUsage / 1024 << "Kb";
|
||||
}
|
||||
|
||||
debug_text_.SetText(ss.str());
|
||||
debug_text_.Update();
|
||||
debug_text_.Reset(ss.str(), debug_text_style_);
|
||||
|
||||
Size layout_size = debug_text_.GetLayoutSize();
|
||||
if (layout_size.x > GetWidth() - 20)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ protected:
|
|||
private:
|
||||
std::locale comma_locale_;
|
||||
BrushPtr background_brush_;
|
||||
TextStyle debug_text_style_;
|
||||
TextLayout debug_text_;
|
||||
List<Time> frame_time_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,23 +40,25 @@ TextActorPtr TextActor::Create(const String& text, const TextStyle& style)
|
|||
TextActorPtr ptr = new (std::nothrow) TextActor;
|
||||
if (ptr)
|
||||
{
|
||||
ptr->SetText(text);
|
||||
ptr->SetStyle(style);
|
||||
ptr->SetText(text);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
TextActor::TextActor()
|
||||
: show_underline_(false)
|
||||
, show_strikethrough_(false)
|
||||
{
|
||||
layout_ = TextLayout::Create();
|
||||
}
|
||||
|
||||
TextActor::~TextActor() {}
|
||||
|
||||
void TextActor::OnRender(RenderContext& ctx)
|
||||
{
|
||||
ctx.DrawTextLayout(text_layout_);
|
||||
if (layout_)
|
||||
{
|
||||
ctx.DrawTextLayout(*layout_);
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::OnUpdate(Duration dt)
|
||||
|
|
@ -66,48 +68,239 @@ void TextActor::OnUpdate(Duration dt)
|
|||
|
||||
void TextActor::UpdateLayout()
|
||||
{
|
||||
if (text_layout_.IsDirty())
|
||||
if (!layout_)
|
||||
return;
|
||||
|
||||
if (layout_->GetDirtyFlag() == TextLayout::DirtyFlag::Updated)
|
||||
{
|
||||
text_layout_.Update();
|
||||
}
|
||||
layout_->SetDirtyFlag(TextLayout::DirtyFlag::Clean);
|
||||
|
||||
if (text_layout_.GetDirtyFlag() & TextLayout::DirtyFlag::Updated)
|
||||
{
|
||||
text_layout_.SetDirtyFlag(TextLayout::DirtyFlag::Clean);
|
||||
|
||||
if (show_underline_)
|
||||
text_layout_.SetUnderline(true, 0, text_layout_.GetText().length());
|
||||
|
||||
if (show_strikethrough_)
|
||||
text_layout_.SetStrikethrough(true, 0, text_layout_.GetText().length());
|
||||
|
||||
SetSize(text_layout_.GetLayoutSize());
|
||||
if (text_.empty())
|
||||
{
|
||||
SetSize(Size());
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSize(layout_->GetLayoutSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TextActor::CheckVisibility(RenderContext& ctx) const
|
||||
{
|
||||
return text_layout_.IsValid() && Actor::CheckVisibility(ctx);
|
||||
return layout_ && layout_->IsValid() && Actor::CheckVisibility(ctx);
|
||||
}
|
||||
|
||||
void TextActor::SetFillColor(Color const& color)
|
||||
{
|
||||
if (!text_layout_.GetFillBrush())
|
||||
BrushPtr brush = layout_->GetDefaultFillBrush();
|
||||
if (brush)
|
||||
{
|
||||
BrushPtr brush = new Brush;
|
||||
text_layout_.SetFillBrush(brush);
|
||||
brush->SetColor(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
layout_->SetDefaultFillBrush(Brush::Create(color));
|
||||
}
|
||||
text_layout_.GetFillBrush()->SetColor(color);
|
||||
}
|
||||
|
||||
void TextActor::SetOutlineColor(Color const& outline_color)
|
||||
{
|
||||
if (!text_layout_.GetOutlineBrush())
|
||||
BrushPtr brush = layout_->GetDefaultOutlineBrush();
|
||||
if (brush)
|
||||
{
|
||||
BrushPtr brush = new Brush;
|
||||
text_layout_.SetOutlineBrush(brush);
|
||||
brush->SetColor(outline_color);
|
||||
}
|
||||
else
|
||||
{
|
||||
layout_->SetDefaultOutlineBrush(Brush::Create(outline_color));
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetTextLayout(TextLayoutPtr layout)
|
||||
{
|
||||
KGE_ASSERT(layout && "TextLayout must not be nullptr");
|
||||
|
||||
if (layout_ != layout)
|
||||
{
|
||||
layout_ = layout;
|
||||
UpdateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetText(String const& text)
|
||||
{
|
||||
if (text_ != text)
|
||||
{
|
||||
text_ = text;
|
||||
layout_->Reset(text_, style_);
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetStyle(const TextStyle& style)
|
||||
{
|
||||
style_ = style;
|
||||
|
||||
if (!text_.empty())
|
||||
{
|
||||
layout_->Reset(text_, style);
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetFont(FontPtr font)
|
||||
{
|
||||
if (style_.font != font)
|
||||
{
|
||||
style_.font = font;
|
||||
if (!text_.empty())
|
||||
{
|
||||
layout_->SetFont(font, { 0, text_.length() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetFontFamily(String const& family)
|
||||
{
|
||||
if (style_.font_family != family)
|
||||
{
|
||||
style_.font_family = family;
|
||||
if (!text_.empty())
|
||||
{
|
||||
layout_->SetFontFamily(family, { 0, text_.length() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetFontSize(float size)
|
||||
{
|
||||
if (style_.font_size != size)
|
||||
{
|
||||
style_.font_size = size;
|
||||
if (!text_.empty())
|
||||
{
|
||||
layout_->SetFontSize(size, { 0, text_.length() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetFontWeight(uint32_t weight)
|
||||
{
|
||||
if (style_.font_weight != weight)
|
||||
{
|
||||
style_.font_weight = weight;
|
||||
if (!text_.empty())
|
||||
{
|
||||
layout_->SetFontWeight(weight, { 0, text_.length() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetItalic(bool italic)
|
||||
{
|
||||
if (style_.italic != italic)
|
||||
{
|
||||
style_.italic = italic;
|
||||
if (!text_.empty())
|
||||
{
|
||||
layout_->SetItalic(italic, { 0, text_.length() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetUnderline(bool enable)
|
||||
{
|
||||
if (style_.show_underline != enable)
|
||||
{
|
||||
style_.show_underline = enable;
|
||||
if (!text_.empty())
|
||||
{
|
||||
layout_->SetUnderline(enable, { 0, text_.length() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetStrikethrough(bool enable)
|
||||
{
|
||||
if (style_.show_strikethrough != enable)
|
||||
{
|
||||
style_.show_strikethrough = enable;
|
||||
if (!text_.empty())
|
||||
{
|
||||
layout_->SetStrikethrough(enable, { 0, text_.length() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetWrapWidth(float wrap_width)
|
||||
{
|
||||
if (style_.wrap_width != wrap_width)
|
||||
{
|
||||
style_.wrap_width = wrap_width;
|
||||
if (!text_.empty())
|
||||
{
|
||||
layout_->SetWrapWidth(wrap_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetLineSpacing(float line_spacing)
|
||||
{
|
||||
if (style_.line_spacing != line_spacing)
|
||||
{
|
||||
style_.line_spacing = line_spacing;
|
||||
if (!text_.empty())
|
||||
{
|
||||
layout_->SetLineSpacing(line_spacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetAlignment(TextAlign align)
|
||||
{
|
||||
if (style_.alignment != align)
|
||||
{
|
||||
style_.alignment = align;
|
||||
if (!text_.empty())
|
||||
{
|
||||
layout_->SetAlignment(align);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetFillBrush(BrushPtr brush)
|
||||
{
|
||||
if (style_.fill_brush != brush)
|
||||
{
|
||||
style_.fill_brush = brush;
|
||||
layout_->SetDefaultFillBrush(brush);
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetOutlineBrush(BrushPtr brush)
|
||||
{
|
||||
if (style_.outline_brush != brush)
|
||||
{
|
||||
style_.outline_brush = brush;
|
||||
layout_->SetDefaultOutlineBrush(brush);
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetOutlineWidth(float outline_width)
|
||||
{
|
||||
if (style_.outline_width != outline_width)
|
||||
{
|
||||
style_.outline_width = outline_width;
|
||||
layout_->SetDefaultOutlineWidth(outline_width);
|
||||
}
|
||||
}
|
||||
|
||||
void TextActor::SetOutlineStrokeStyle(StrokeStylePtr stroke)
|
||||
{
|
||||
if (style_.outline_stroke != stroke)
|
||||
{
|
||||
style_.outline_stroke = stroke;
|
||||
layout_->SetDefaultOutlineStrokeStyle(stroke);
|
||||
}
|
||||
text_layout_.GetOutlineBrush()->SetColor(outline_color);
|
||||
}
|
||||
|
||||
} // namespace kiwano
|
||||
|
|
|
|||
|
|
@ -64,11 +64,7 @@ public:
|
|||
|
||||
/// \~chinese
|
||||
/// @brief 获取文本布局
|
||||
const TextLayout& GetLayout() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取文本布局
|
||||
TextLayout& GetLayout();
|
||||
TextLayoutPtr GetLayout() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取文本布局大小
|
||||
|
|
@ -82,6 +78,10 @@ public:
|
|||
/// @brief 获取描边画刷
|
||||
BrushPtr GetOutlineBrush() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取描边线条样式
|
||||
StrokeStylePtr GetOutlineStrokeStyle() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取字体
|
||||
FontPtr GetFont() const;
|
||||
|
|
@ -143,12 +143,12 @@ public:
|
|||
void SetOutlineColor(Color const& outline_color);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文字描边线宽
|
||||
void SetOutlineWidth(float outline_width);
|
||||
/// @brief 设置描边线条样式
|
||||
void SetOutlineStrokeStyle(StrokeStylePtr stroke);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文字描边线相交样式
|
||||
void SetOutlineStroke(StrokeStylePtr outline_stroke);
|
||||
/// @brief 设置文字描边线宽
|
||||
void SetOutlineWidth(float outline_width);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置是否显示下划线(默认值为 false)
|
||||
|
|
@ -158,6 +158,10 @@ public:
|
|||
/// @brief 设置是否显示删除线(默认值为 false)
|
||||
void SetStrikethrough(bool enable);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文本布局
|
||||
void SetTextLayout(TextLayoutPtr layout);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 更新文字布局
|
||||
/// @details 文字布局是懒更新的,手动更新文字布局以更新节点状态
|
||||
|
|
@ -171,130 +175,55 @@ protected:
|
|||
bool CheckVisibility(RenderContext& ctx) const override;
|
||||
|
||||
private:
|
||||
bool show_underline_;
|
||||
bool show_strikethrough_;
|
||||
TextLayout text_layout_;
|
||||
String text_;
|
||||
TextStyle style_;
|
||||
TextLayoutPtr layout_;
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
inline const String& TextActor::GetText() const
|
||||
{
|
||||
return text_layout_.GetText();
|
||||
return text_;
|
||||
}
|
||||
|
||||
inline FontPtr TextActor::GetFont() const
|
||||
{
|
||||
return text_layout_.GetStyle().font;
|
||||
return style_.font;
|
||||
}
|
||||
|
||||
inline const TextStyle& TextActor::GetStyle() const
|
||||
{
|
||||
return text_layout_.GetStyle();
|
||||
return style_;
|
||||
}
|
||||
|
||||
inline const TextLayout& TextActor::GetLayout() const
|
||||
inline TextLayoutPtr TextActor::GetLayout() const
|
||||
{
|
||||
return text_layout_;
|
||||
}
|
||||
|
||||
inline TextLayout& TextActor::GetLayout()
|
||||
{
|
||||
return text_layout_;
|
||||
return layout_;
|
||||
}
|
||||
|
||||
inline Size TextActor::GetLayoutSize() const
|
||||
{
|
||||
return text_layout_.GetLayoutSize();
|
||||
if (layout_)
|
||||
{
|
||||
return layout_->GetLayoutSize();
|
||||
}
|
||||
return Size();
|
||||
}
|
||||
|
||||
inline BrushPtr TextActor::GetFillBrush() const
|
||||
{
|
||||
return text_layout_.GetFillBrush();
|
||||
return style_.fill_brush;
|
||||
}
|
||||
|
||||
inline BrushPtr TextActor::GetOutlineBrush() const
|
||||
{
|
||||
return text_layout_.GetOutlineBrush();
|
||||
return style_.outline_brush;
|
||||
}
|
||||
|
||||
inline void TextActor::SetText(String const& text)
|
||||
inline StrokeStylePtr TextActor::GetOutlineStrokeStyle() const
|
||||
{
|
||||
text_layout_.SetText(text);
|
||||
return style_.outline_stroke;
|
||||
}
|
||||
|
||||
inline void TextActor::SetStyle(const TextStyle& style)
|
||||
{
|
||||
text_layout_.SetStyle(style);
|
||||
}
|
||||
|
||||
inline void TextActor::SetFont(FontPtr font)
|
||||
{
|
||||
text_layout_.SetFont(font);
|
||||
}
|
||||
|
||||
inline void TextActor::SetFontFamily(String const& family)
|
||||
{
|
||||
text_layout_.SetFontFamily(family);
|
||||
}
|
||||
|
||||
inline void TextActor::SetFontSize(float size)
|
||||
{
|
||||
text_layout_.SetFontSize(size);
|
||||
}
|
||||
|
||||
inline void TextActor::SetFontWeight(uint32_t weight)
|
||||
{
|
||||
text_layout_.SetFontWeight(weight);
|
||||
}
|
||||
|
||||
inline void TextActor::SetItalic(bool italic)
|
||||
{
|
||||
text_layout_.SetItalic(italic);
|
||||
}
|
||||
|
||||
inline void TextActor::SetWrapWidth(float wrap_width)
|
||||
{
|
||||
text_layout_.SetWrapWidth(wrap_width);
|
||||
}
|
||||
|
||||
inline void TextActor::SetLineSpacing(float line_spacing)
|
||||
{
|
||||
text_layout_.SetLineSpacing(line_spacing);
|
||||
}
|
||||
|
||||
inline void TextActor::SetAlignment(TextAlign align)
|
||||
{
|
||||
text_layout_.SetAlignment(align);
|
||||
}
|
||||
|
||||
inline void TextActor::SetUnderline(bool enable)
|
||||
{
|
||||
show_underline_ = enable;
|
||||
}
|
||||
|
||||
inline void TextActor::SetStrikethrough(bool enable)
|
||||
{
|
||||
show_strikethrough_ = enable;
|
||||
}
|
||||
|
||||
inline void TextActor::SetFillBrush(BrushPtr brush)
|
||||
{
|
||||
text_layout_.SetFillBrush(brush);
|
||||
}
|
||||
|
||||
inline void TextActor::SetOutlineBrush(BrushPtr brush)
|
||||
{
|
||||
text_layout_.SetOutlineBrush(brush);
|
||||
}
|
||||
|
||||
inline void TextActor::SetOutlineWidth(float outline_width)
|
||||
{
|
||||
text_layout_.SetOutlineWidth(outline_width);
|
||||
}
|
||||
|
||||
inline void TextActor::SetOutlineStroke(StrokeStylePtr outline_stroke)
|
||||
{
|
||||
text_layout_.SetOutlineStroke(outline_stroke);
|
||||
}
|
||||
} // namespace kiwano
|
||||
|
|
|
|||
|
|
@ -116,27 +116,31 @@ void RenderContextImpl::DrawTextLayout(TextLayout const& layout, Point const& of
|
|||
|
||||
if (layout.IsValid())
|
||||
{
|
||||
ComPtr<ID2D1Brush> fill_brush;
|
||||
ComPtr<ID2D1Brush> outline_brush;
|
||||
const TextStyle& style = layout.GetStyle();
|
||||
ComPtr<ID2D1Brush> fill_brush;
|
||||
ComPtr<ID2D1Brush> outline_brush;
|
||||
ComPtr<ID2D1StrokeStyle> outline_stroke;
|
||||
|
||||
if (style.fill_brush)
|
||||
if (layout.GetDefaultFillBrush())
|
||||
{
|
||||
fill_brush = style.fill_brush->GetBrush();
|
||||
fill_brush = layout.GetDefaultFillBrush()->GetBrush();
|
||||
fill_brush->SetOpacity(brush_opacity_);
|
||||
}
|
||||
|
||||
if (style.outline_brush)
|
||||
if (layout.GetDefaultOutlineBrush())
|
||||
{
|
||||
outline_brush = style.outline_brush->GetBrush();
|
||||
outline_brush = layout.GetDefaultOutlineBrush()->GetBrush();
|
||||
outline_brush->SetOpacity(brush_opacity_);
|
||||
}
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
ID2D1StrokeStyle* stroke_style = style.outline_stroke ? style.outline_stroke->GetStrokeStyle().Get() : nullptr;
|
||||
if (layout.GetDefaultOutlineStrokeStyle())
|
||||
{
|
||||
outline_stroke = layout.GetDefaultOutlineStrokeStyle()->GetStrokeStyle();
|
||||
}
|
||||
|
||||
hr = text_renderer_->DrawTextLayout(layout.GetTextLayout().Get(), offset.x, offset.y, fill_brush.Get(),
|
||||
outline_brush.Get(), style.outline_width, stroke_style);
|
||||
float outline_width = layout.GetDefaultOutlineWidth();
|
||||
|
||||
HRESULT hr = text_renderer_->DrawTextLayout(layout.GetTextLayout().Get(), offset.x, offset.y, fill_brush.Get(),
|
||||
outline_brush.Get(), outline_width, outline_stroke.Get());
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -587,7 +587,7 @@ void RendererImpl::CreateFontCollection(Font& font, Resource const& res)
|
|||
KGE_THROW_IF_FAILED(hr, "Create font collection failed");
|
||||
}
|
||||
|
||||
void RendererImpl::CreateTextLayout(TextLayout& layout)
|
||||
void RendererImpl::CreateTextLayout(TextLayout& layout, const String& content, const TextStyle& style)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
if (!d2d_res_)
|
||||
|
|
@ -595,111 +595,39 @@ void RendererImpl::CreateTextLayout(TextLayout& layout)
|
|||
hr = E_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (layout.GetText().empty())
|
||||
if (content.empty())
|
||||
{
|
||||
layout.SetTextFormat(nullptr);
|
||||
layout.SetTextLayout(nullptr);
|
||||
layout.SetDirtyFlag(TextLayout::DirtyFlag::Updated);
|
||||
return;
|
||||
}
|
||||
|
||||
const TextStyle& style = layout.GetStyle();
|
||||
|
||||
if (!layout.GetTextFormat() || (layout.GetDirtyFlag() & TextLayout::DirtyFlag::DirtyFormat))
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
WideString font_family = style.font_family.empty() ? L"" : string::ToWide(style.font_family);
|
||||
DWRITE_FONT_WEIGHT font_weight = DWRITE_FONT_WEIGHT(style.font_weight);
|
||||
DWRITE_FONT_STYLE font_style = style.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL;
|
||||
IDWriteFontCollection* collection = style.font ? style.font->GetCollection().Get() : nullptr;
|
||||
ComPtr<IDWriteTextFormat> format;
|
||||
WideString font_family = style.font_family.empty() ? L"" : string::ToWide(style.font_family);
|
||||
DWRITE_FONT_WEIGHT font_weight = DWRITE_FONT_WEIGHT(style.font_weight);
|
||||
DWRITE_FONT_STYLE font_style = style.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL;
|
||||
IDWriteFontCollection* collection = style.font ? style.font->GetCollection().Get() : nullptr;
|
||||
|
||||
ComPtr<IDWriteTextFormat> output;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = d2d_res_->CreateTextFormat(output, font_family.c_str(), collection, font_weight, font_style,
|
||||
DWRITE_FONT_STRETCH_NORMAL, style.font_size);
|
||||
}
|
||||
hr = d2d_res_->CreateTextFormat(format, font_family.c_str(), collection, font_weight, font_style,
|
||||
DWRITE_FONT_STRETCH_NORMAL, style.font_size);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
layout.SetTextFormat(output);
|
||||
layout.SetDirtyFlag(layout.GetDirtyFlag() | TextLayout::DirtyFlag::DirtyLayout);
|
||||
}
|
||||
}
|
||||
ComPtr<IDWriteTextLayout> output;
|
||||
WideString wide = string::ToWide(content);
|
||||
|
||||
if (layout.GetDirtyFlag() & TextLayout::DirtyFlag::DirtyLayout)
|
||||
{
|
||||
ComPtr<IDWriteTextLayout> output;
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
WideString text = string::ToWide(layout.GetText());
|
||||
|
||||
hr = d2d_res_->CreateTextLayout(output, text.c_str(), text.length(), layout.GetTextFormat());
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = output->SetTextAlignment(DWRITE_TEXT_ALIGNMENT(style.alignment));
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
float wrap_width = style.wrap_width;
|
||||
bool enable_wrapping = (wrap_width > 0);
|
||||
hr = d2d_res_->CreateTextLayout(output, wide.c_str(), wide.length(), format);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
if (enable_wrapping)
|
||||
{
|
||||
hr = output->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = output->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
|
||||
}
|
||||
layout.SetTextLayout(output);
|
||||
layout.SetDirtyFlag(TextLayout::DirtyFlag::Updated);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
if (enable_wrapping)
|
||||
{
|
||||
hr = output->SetMaxWidth(wrap_width);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fix the layout width when the text does not wrap
|
||||
DWRITE_TEXT_METRICS metrics;
|
||||
hr = output->GetMetrics(&metrics);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = output->SetMaxWidth(metrics.width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
float spacing = style.line_spacing;
|
||||
if (spacing == 0.f)
|
||||
{
|
||||
hr = output->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_DEFAULT, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = output->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM, spacing, spacing * 0.8f);
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
layout.SetTextLayout(output);
|
||||
}
|
||||
}
|
||||
|
||||
layout.SetDirtyFlag(TextLayout::DirtyFlag::Updated);
|
||||
|
||||
KGE_THROW_IF_FAILED(hr, "Create text layout failed");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ public:
|
|||
|
||||
void CreateFontCollection(Font& font, Resource const& res) override;
|
||||
|
||||
void CreateTextLayout(TextLayout& layout) override;
|
||||
void CreateTextLayout(TextLayout& layout, const String& content, const TextStyle& style) override;
|
||||
|
||||
void CreateLineShape(Shape& shape, Point const& begin_pos, Point const& end_pos) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ public:
|
|||
STDMETHOD(CreateDeviceResources)(_In_ ID2D1RenderTarget* pRT);
|
||||
|
||||
STDMETHOD(DrawTextLayout)
|
||||
(_In_ IDWriteTextLayout* pTextLayout, float fOriginX, float fOriginY, _In_opt_ ID2D1Brush* pFillBrush,
|
||||
_In_opt_ ID2D1Brush* pOutlineBrush, float fOutlineWidth, _In_opt_ ID2D1StrokeStyle* pStrokeStyle);
|
||||
(_In_ IDWriteTextLayout* pTextLayout, float fOriginX, float fOriginY, _In_opt_ ID2D1Brush* pDefaultFillBrush,
|
||||
_In_opt_ ID2D1Brush* pDefaultOutlineBrush, float fDefaultOutlineWidth, _In_opt_ ID2D1StrokeStyle* pStrokeStyle);
|
||||
|
||||
STDMETHOD(DrawGlyphRun)
|
||||
(__maybenull void* clientDrawingContext, float baselineOriginX, float baselineOriginY,
|
||||
|
|
@ -68,12 +68,12 @@ public:
|
|||
private:
|
||||
unsigned long cRefCount_;
|
||||
uint32_t cPrimitivesCount_;
|
||||
float fOutlineWidth_;
|
||||
float fDefaultOutlineWidth_;
|
||||
ComPtr<ID2D1Factory> pFactory_;
|
||||
ComPtr<ID2D1RenderTarget> pRT_;
|
||||
ComPtr<ID2D1Brush> pFillBrush_;
|
||||
ComPtr<ID2D1Brush> pOutlineBrush_;
|
||||
ComPtr<ID2D1StrokeStyle> pCurrStrokeStyle_;
|
||||
ComPtr<ID2D1Brush> pDefaultFillBrush_;
|
||||
ComPtr<ID2D1Brush> pDefaultOutlineBrush_;
|
||||
ComPtr<ID2D1StrokeStyle> pDefaultStrokeStyle_;
|
||||
};
|
||||
|
||||
HRESULT ITextRenderer::Create(_Out_ ITextRenderer** ppTextRenderer, _In_ ID2D1RenderTarget* pRT)
|
||||
|
|
@ -108,7 +108,7 @@ HRESULT ITextRenderer::Create(_Out_ ITextRenderer** ppTextRenderer, _In_ ID2D1Re
|
|||
TextRenderer::TextRenderer()
|
||||
: cRefCount_(0)
|
||||
, cPrimitivesCount_(0)
|
||||
, fOutlineWidth_(1)
|
||||
, fDefaultOutlineWidth_(1)
|
||||
{
|
||||
if (pRT_)
|
||||
{
|
||||
|
|
@ -134,19 +134,20 @@ STDMETHODIMP TextRenderer::CreateDeviceResources(_In_ ID2D1RenderTarget* pRT)
|
|||
}
|
||||
|
||||
STDMETHODIMP TextRenderer::DrawTextLayout(_In_ IDWriteTextLayout* pTextLayout, float fOriginX, float fOriginY,
|
||||
_In_opt_ ID2D1Brush* pFillBrush, _In_opt_ ID2D1Brush* pOutlineBrush,
|
||||
float fOutlineWidth, _In_opt_ ID2D1StrokeStyle* pStrokeStyle)
|
||||
_In_opt_ ID2D1Brush* pDefaultFillBrush,
|
||||
_In_opt_ ID2D1Brush* pDefaultOutlineBrush, float fDefaultOutlineWidth,
|
||||
_In_opt_ ID2D1StrokeStyle* pDefaultStrokeStyle)
|
||||
{
|
||||
if (!pTextLayout)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
cPrimitivesCount_ = 0;
|
||||
pFillBrush_ = pFillBrush;
|
||||
pOutlineBrush_ = pOutlineBrush;
|
||||
fOutlineWidth_ = fOutlineWidth;
|
||||
pCurrStrokeStyle_ = pStrokeStyle;
|
||||
cPrimitivesCount_ = 0;
|
||||
pDefaultFillBrush_ = pDefaultFillBrush;
|
||||
pDefaultOutlineBrush_ = pDefaultOutlineBrush;
|
||||
fDefaultOutlineWidth_ = fDefaultOutlineWidth;
|
||||
pDefaultStrokeStyle_ = pDefaultStrokeStyle;
|
||||
|
||||
return pTextLayout->Draw(nullptr, this, fOriginX, fOriginY);
|
||||
}
|
||||
|
|
@ -160,17 +161,19 @@ STDMETHODIMP TextRenderer::DrawGlyphRun(__maybenull void* clientDrawingContext,
|
|||
KGE_NOT_USED(clientDrawingContext);
|
||||
KGE_NOT_USED(measuringMode);
|
||||
KGE_NOT_USED(glyphRunDescription);
|
||||
KGE_NOT_USED(clientDrawingEffect);
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (pOutlineBrush_)
|
||||
if (pDefaultOutlineBrush_)
|
||||
{
|
||||
ComPtr<ID2D1GeometrySink> pSink;
|
||||
ComPtr<ID2D1PathGeometry> pPathGeometry;
|
||||
ComPtr<ID2D1TransformedGeometry> pTransformedGeometry;
|
||||
|
||||
hr = pFactory_->CreatePathGeometry(&pPathGeometry);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = pFactory_->CreatePathGeometry(&pPathGeometry);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
|
@ -200,9 +203,9 @@ STDMETHODIMP TextRenderer::DrawGlyphRun(__maybenull void* clientDrawingContext,
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
pRT_->DrawGeometry(pTransformedGeometry.Get(), pOutlineBrush_.Get(),
|
||||
fOutlineWidth_ * 2, // twice width for widening
|
||||
pCurrStrokeStyle_.Get());
|
||||
pRT_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(),
|
||||
fDefaultOutlineWidth_ * 2, // twice width for widening
|
||||
pDefaultStrokeStyle_.Get());
|
||||
|
||||
++cPrimitivesCount_;
|
||||
}
|
||||
|
|
@ -210,11 +213,24 @@ STDMETHODIMP TextRenderer::DrawGlyphRun(__maybenull void* clientDrawingContext,
|
|||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr) && pFillBrush_)
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
pRT_->DrawGlyphRun(D2D1::Point2F(baselineOriginX, baselineOriginY), glyphRun, pFillBrush_.Get());
|
||||
ComPtr<ID2D1Brush> pCurrentFillBrush;
|
||||
if (clientDrawingEffect)
|
||||
{
|
||||
hr = clientDrawingEffect->QueryInterface<ID2D1Brush>(&pCurrentFillBrush);
|
||||
}
|
||||
else
|
||||
{
|
||||
pCurrentFillBrush = pDefaultFillBrush_;
|
||||
}
|
||||
|
||||
++cPrimitivesCount_;
|
||||
if (SUCCEEDED(hr) && pCurrentFillBrush)
|
||||
{
|
||||
pRT_->DrawGlyphRun(D2D1::Point2F(baselineOriginX, baselineOriginY), glyphRun, pCurrentFillBrush.Get());
|
||||
|
||||
++cPrimitivesCount_;
|
||||
}
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
|
@ -224,34 +240,51 @@ STDMETHODIMP TextRenderer::DrawUnderline(__maybenull void* clientDrawingContext,
|
|||
IUnknown* clientDrawingEffect)
|
||||
{
|
||||
KGE_NOT_USED(clientDrawingContext);
|
||||
KGE_NOT_USED(clientDrawingEffect);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
D2D1_RECT_F rect = D2D1::RectF(0, underline->offset, underline->width, underline->offset + underline->thickness);
|
||||
|
||||
ComPtr<ID2D1RectangleGeometry> pRectangleGeometry;
|
||||
hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry);
|
||||
|
||||
D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY);
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
ComPtr<ID2D1RectangleGeometry> pRectangleGeometry;
|
||||
ComPtr<ID2D1TransformedGeometry> pTransformedGeometry;
|
||||
if (SUCCEEDED(hr))
|
||||
ComPtr<ID2D1Brush> pCurrentFillBrush;
|
||||
|
||||
if (clientDrawingEffect)
|
||||
{
|
||||
hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry);
|
||||
hr = clientDrawingEffect->QueryInterface<ID2D1Brush>(&pCurrentFillBrush);
|
||||
}
|
||||
else
|
||||
{
|
||||
pCurrentFillBrush = pDefaultFillBrush_;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr) && pOutlineBrush_)
|
||||
if (pCurrentFillBrush || pDefaultOutlineBrush_)
|
||||
{
|
||||
pRT_->DrawGeometry(pTransformedGeometry.Get(), pOutlineBrush_.Get(), fOutlineWidth_ * 2,
|
||||
pCurrStrokeStyle_.Get());
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
D2D1_RECT_F rect =
|
||||
D2D1::RectF(0, underline->offset, underline->width, underline->offset + underline->thickness);
|
||||
|
||||
hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY);
|
||||
|
||||
hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry);
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr) && pDefaultOutlineBrush_)
|
||||
{
|
||||
pRT_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_ * 2,
|
||||
pDefaultStrokeStyle_.Get());
|
||||
|
||||
++cPrimitivesCount_;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr) && pFillBrush_)
|
||||
if (SUCCEEDED(hr) && pCurrentFillBrush)
|
||||
{
|
||||
pRT_->FillGeometry(pTransformedGeometry.Get(), pFillBrush_.Get());
|
||||
pRT_->FillGeometry(pTransformedGeometry.Get(), pCurrentFillBrush.Get());
|
||||
|
||||
++cPrimitivesCount_;
|
||||
}
|
||||
|
|
@ -263,35 +296,51 @@ STDMETHODIMP TextRenderer::DrawStrikethrough(__maybenull void* clientDrawingCont
|
|||
IUnknown* clientDrawingEffect)
|
||||
{
|
||||
KGE_NOT_USED(clientDrawingContext);
|
||||
KGE_NOT_USED(clientDrawingEffect);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
D2D1_RECT_F rect =
|
||||
D2D1::RectF(0, strikethrough->offset, strikethrough->width, strikethrough->offset + strikethrough->thickness);
|
||||
|
||||
ComPtr<ID2D1RectangleGeometry> pRectangleGeometry;
|
||||
hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry);
|
||||
|
||||
D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY);
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
ComPtr<ID2D1RectangleGeometry> pRectangleGeometry;
|
||||
ComPtr<ID2D1TransformedGeometry> pTransformedGeometry;
|
||||
if (SUCCEEDED(hr))
|
||||
ComPtr<ID2D1Brush> pCurrentFillBrush;
|
||||
|
||||
if (clientDrawingEffect)
|
||||
{
|
||||
hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry);
|
||||
hr = clientDrawingEffect->QueryInterface<ID2D1Brush>(&pCurrentFillBrush);
|
||||
}
|
||||
else
|
||||
{
|
||||
pCurrentFillBrush = pDefaultFillBrush_;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr) && pOutlineBrush_)
|
||||
if (pCurrentFillBrush || pDefaultOutlineBrush_)
|
||||
{
|
||||
pRT_->DrawGeometry(pTransformedGeometry.Get(), pOutlineBrush_.Get(), fOutlineWidth_ * 2,
|
||||
pCurrStrokeStyle_.Get());
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
D2D1_RECT_F rect = D2D1::RectF(0, strikethrough->offset, strikethrough->width,
|
||||
strikethrough->offset + strikethrough->thickness);
|
||||
|
||||
hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY);
|
||||
|
||||
hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry);
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr) && pDefaultOutlineBrush_)
|
||||
{
|
||||
pRT_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_ * 2,
|
||||
pDefaultStrokeStyle_.Get());
|
||||
|
||||
++cPrimitivesCount_;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr) && pFillBrush_)
|
||||
if (SUCCEEDED(hr) && pCurrentFillBrush)
|
||||
{
|
||||
pRT_->FillGeometry(pTransformedGeometry.Get(), pFillBrush_.Get());
|
||||
pRT_->FillGeometry(pTransformedGeometry.Get(), pCurrentFillBrush.Get());
|
||||
|
||||
++cPrimitivesCount_;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,9 @@ public:
|
|||
static KGE_API HRESULT Create(_Out_ ITextRenderer * *ppTextRenderer, _In_ ID2D1RenderTarget * pRT);
|
||||
|
||||
STDMETHOD(DrawTextLayout)
|
||||
(_In_ IDWriteTextLayout * pTextLayout, float fOriginX, float fOriginY, _In_opt_ ID2D1Brush* pFillBrush,
|
||||
_In_opt_ ID2D1Brush* pOutlineBrush, float fOutlineWidth, _In_opt_ ID2D1StrokeStyle* pStrokeStyle) PURE;
|
||||
(_In_ IDWriteTextLayout * pTextLayout, float fOriginX, float fOriginY, _In_opt_ ID2D1Brush* pDefaultFillBrush,
|
||||
_In_opt_ ID2D1Brush* pDefaultOutlineBrush, float fDefaultOutlineWidth,
|
||||
_In_opt_ ID2D1StrokeStyle* pDefaultStrokeStyle) PURE;
|
||||
|
||||
STDMETHOD_(uint32_t, GetLastPrimitivesCount)() PURE;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ bool Font::Load(String const& file)
|
|||
{
|
||||
Renderer::GetInstance().CreateFontCollection(*this, file);
|
||||
}
|
||||
catch (std::runtime_error&)
|
||||
catch (RuntimeError&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ bool Font::Load(Resource const& resource)
|
|||
{
|
||||
Renderer::GetInstance().CreateFontCollection(*this, resource);
|
||||
}
|
||||
catch (std::runtime_error&)
|
||||
catch (RuntimeError&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,8 +116,10 @@ public:
|
|||
/// \~chinese
|
||||
/// @brief 创建文字布局内部资源
|
||||
/// @param[out] layout 字体布局
|
||||
/// @param text 文字内容
|
||||
/// @param style 文本样式
|
||||
/// @throw kiwano::SystemError 创建失败时抛出
|
||||
virtual void CreateTextLayout(TextLayout& layout) = 0;
|
||||
virtual void CreateTextLayout(TextLayout& layout, const String& content, const TextStyle& style) = 0;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 创建线段形状内部资源
|
||||
|
|
|
|||
|
|
@ -23,81 +23,52 @@
|
|||
|
||||
namespace kiwano
|
||||
{
|
||||
TextLayoutPtr TextLayout::Create()
|
||||
{
|
||||
TextLayoutPtr ptr = new (std::nothrow) TextLayout;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
TextLayoutPtr TextLayout::Create(const String& content, const TextStyle& style)
|
||||
{
|
||||
TextLayoutPtr ptr = new (std::nothrow) TextLayout;
|
||||
if (ptr)
|
||||
{
|
||||
ptr->Reset(content, style);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
TextLayout::TextLayout()
|
||||
: dirty_flag_(DirtyFlag::Clean)
|
||||
, default_outline_width_(0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
void TextLayout::Update()
|
||||
void TextLayout::Reset(const String& text, const TextStyle& style)
|
||||
{
|
||||
if (!IsDirty())
|
||||
return;
|
||||
|
||||
Renderer::GetInstance().CreateTextLayout(*this);
|
||||
}
|
||||
|
||||
void TextLayout::SetText(const String& text)
|
||||
{
|
||||
text_ = text;
|
||||
dirty_flag_ |= DirtyFlag::DirtyLayout;
|
||||
}
|
||||
|
||||
void TextLayout::SetStyle(const TextStyle& style)
|
||||
{
|
||||
style_ = style;
|
||||
dirty_flag_ |= DirtyFlag::DirtyLayout;
|
||||
}
|
||||
|
||||
void TextLayout::SetFont(FontPtr font)
|
||||
{
|
||||
if (style_.font != font)
|
||||
if (!text.empty())
|
||||
{
|
||||
style_.font = font;
|
||||
dirty_flag_ |= DirtyFlag::DirtyFormat;
|
||||
Renderer::GetInstance().CreateTextLayout(*this, text, style);
|
||||
|
||||
SetAlignment(style.alignment);
|
||||
SetWrapWidth(style.wrap_width);
|
||||
SetLineSpacing(style.line_spacing);
|
||||
SetUnderline(style.show_underline, { 0, text.length() });
|
||||
SetStrikethrough(style.show_strikethrough, { 0, text.length() });
|
||||
SetDefaultFillBrush(style.fill_brush);
|
||||
SetDefaultOutlineBrush(style.outline_brush);
|
||||
SetDefaultOutlineWidth(style.outline_width);
|
||||
SetDefaultOutlineStrokeStyle(style.outline_stroke);
|
||||
}
|
||||
}
|
||||
|
||||
void TextLayout::SetFontFamily(String const& family)
|
||||
{
|
||||
if (style_.font_family != family)
|
||||
else
|
||||
{
|
||||
style_.font_family = family;
|
||||
dirty_flag_ |= DirtyFlag::DirtyFormat;
|
||||
}
|
||||
}
|
||||
|
||||
void TextLayout::SetFontSize(float size)
|
||||
{
|
||||
if (style_.font_size != size)
|
||||
{
|
||||
style_.font_size = size;
|
||||
dirty_flag_ |= DirtyFlag::DirtyFormat;
|
||||
}
|
||||
}
|
||||
|
||||
void TextLayout::SetFontWeight(uint32_t weight)
|
||||
{
|
||||
if (style_.font_weight != weight)
|
||||
{
|
||||
style_.font_weight = weight;
|
||||
dirty_flag_ |= DirtyFlag::DirtyFormat;
|
||||
}
|
||||
}
|
||||
|
||||
void TextLayout::SetItalic(bool italic)
|
||||
{
|
||||
if (style_.italic != italic)
|
||||
{
|
||||
style_.italic = italic;
|
||||
dirty_flag_ |= DirtyFlag::DirtyFormat;
|
||||
Clear();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t TextLayout::GetLineCount() const
|
||||
{
|
||||
// Force to update layout
|
||||
const_cast<TextLayout*>(this)->Update();
|
||||
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
if (text_layout_)
|
||||
{
|
||||
|
|
@ -115,9 +86,6 @@ uint32_t TextLayout::GetLineCount() const
|
|||
|
||||
Size TextLayout::GetLayoutSize() const
|
||||
{
|
||||
// Force to update layout
|
||||
const_cast<TextLayout*>(this)->Update();
|
||||
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
if (text_layout_)
|
||||
{
|
||||
|
|
@ -134,64 +102,265 @@ Size TextLayout::GetLayoutSize() const
|
|||
return Size();
|
||||
}
|
||||
|
||||
void TextLayout::SetWrapWidth(float wrap_width)
|
||||
void TextLayout::SetFont(FontPtr font, TextRange range)
|
||||
{
|
||||
if (style_.wrap_width != wrap_width)
|
||||
{
|
||||
style_.wrap_width = wrap_width;
|
||||
dirty_flag_ |= DirtyFlag::DirtyLayout;
|
||||
}
|
||||
}
|
||||
|
||||
void TextLayout::SetLineSpacing(float line_spacing)
|
||||
{
|
||||
if (style_.line_spacing != line_spacing)
|
||||
{
|
||||
style_.line_spacing = line_spacing;
|
||||
dirty_flag_ |= DirtyFlag::DirtyLayout;
|
||||
}
|
||||
}
|
||||
|
||||
void TextLayout::SetAlignment(TextAlign align)
|
||||
{
|
||||
if (style_.alignment != align)
|
||||
{
|
||||
style_.alignment = align;
|
||||
dirty_flag_ |= DirtyFlag::DirtyLayout;
|
||||
}
|
||||
}
|
||||
|
||||
void TextLayout::SetUnderline(bool enable, uint32_t start, uint32_t length)
|
||||
{
|
||||
// Force to update layout
|
||||
Update();
|
||||
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
HRESULT hr = text_layout_ ? S_OK : E_FAIL;
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
KGE_ASSERT(text_layout_);
|
||||
if (text_layout_)
|
||||
{
|
||||
hr = text_layout_->SetUnderline(enable, { start, length });
|
||||
IDWriteFontCollection* collection = font ? font->GetCollection().Get() : nullptr;
|
||||
|
||||
HRESULT hr = text_layout_->SetFontCollection(collection, { range.start, range.length });
|
||||
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontCollection failed");
|
||||
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
}
|
||||
#else
|
||||
// not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetFontFamily(String const& family, TextRange range)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
KGE_ASSERT(text_layout_);
|
||||
if (text_layout_)
|
||||
{
|
||||
WideString font_family = family.empty() ? L"" : string::ToWide(family);
|
||||
|
||||
HRESULT hr = text_layout_->SetFontFamilyName(font_family.c_str(), { range.start, range.length });
|
||||
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontFamilyName failed");
|
||||
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
}
|
||||
#else
|
||||
// not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetFontSize(float size, TextRange range)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
KGE_ASSERT(text_layout_);
|
||||
if (text_layout_)
|
||||
{
|
||||
HRESULT hr = text_layout_->SetFontSize(size, { range.start, range.length });
|
||||
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontSize failed");
|
||||
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
}
|
||||
#else
|
||||
// not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetFontWeight(uint32_t weight, TextRange range)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
KGE_ASSERT(text_layout_);
|
||||
if (text_layout_)
|
||||
{
|
||||
DWRITE_FONT_WEIGHT font_weight = DWRITE_FONT_WEIGHT(weight);
|
||||
|
||||
HRESULT hr = text_layout_->SetFontWeight(font_weight, { range.start, range.length });
|
||||
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontWeight failed");
|
||||
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
}
|
||||
#else
|
||||
// not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetItalic(bool italic, TextRange range)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
KGE_ASSERT(text_layout_);
|
||||
if (text_layout_)
|
||||
{
|
||||
DWRITE_FONT_STYLE font_style = italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL;
|
||||
|
||||
HRESULT hr = text_layout_->SetFontStyle(font_style, { range.start, range.length });
|
||||
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontStyle failed");
|
||||
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
}
|
||||
#else
|
||||
// not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetUnderline(bool enable, TextRange range)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
KGE_ASSERT(text_layout_);
|
||||
if (text_layout_)
|
||||
{
|
||||
HRESULT hr = text_layout_->SetUnderline(enable, { range.start, range.length });
|
||||
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetUnderline failed");
|
||||
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
}
|
||||
KGE_THROW_IF_FAILED(hr, "Apply underline style to text layout failed");
|
||||
#else
|
||||
return; // not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetStrikethrough(bool enable, uint32_t start, uint32_t length)
|
||||
void TextLayout::SetStrikethrough(bool enable, TextRange range)
|
||||
{
|
||||
// Force to update layout
|
||||
Update();
|
||||
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
HRESULT hr = text_layout_ ? S_OK : E_FAIL;
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
KGE_ASSERT(text_layout_);
|
||||
if (text_layout_)
|
||||
{
|
||||
hr = text_layout_->SetStrikethrough(enable, { start, length });
|
||||
HRESULT hr = text_layout_->SetStrikethrough(enable, { range.start, range.length });
|
||||
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetStrikethrough failed");
|
||||
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
}
|
||||
#else
|
||||
return; // not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetFillBrush(BrushPtr brush, TextRange range)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
KGE_ASSERT(text_layout_);
|
||||
if (text_layout_)
|
||||
{
|
||||
HRESULT hr = text_layout_->SetDrawingEffect(brush->GetBrush().Get(), { range.start, range.length });
|
||||
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetDrawingEffect failed");
|
||||
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
}
|
||||
#else
|
||||
return; // not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetOutlineBrush(BrushPtr brush, TextRange range)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
// TODO
|
||||
KGE_NOT_USED(range);
|
||||
SetDefaultOutlineBrush(brush);
|
||||
#else
|
||||
return; // not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetOutlineWidth(float width, TextRange range)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
// TODO
|
||||
KGE_NOT_USED(range);
|
||||
SetDefaultOutlineWidth(width);
|
||||
#else
|
||||
return; // not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetOutlineStrokeStyle(StrokeStylePtr stroke, TextRange range)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
// TODO
|
||||
KGE_NOT_USED(range);
|
||||
SetDefaultOutlineStrokeStyle(stroke);
|
||||
#else
|
||||
return; // not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetAlignment(TextAlign align)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
KGE_ASSERT(text_layout_);
|
||||
if (text_layout_)
|
||||
{
|
||||
DWRITE_TEXT_ALIGNMENT alignment = DWRITE_TEXT_ALIGNMENT();
|
||||
switch (align)
|
||||
{
|
||||
case TextAlign::Left:
|
||||
alignment = DWRITE_TEXT_ALIGNMENT_LEADING;
|
||||
break;
|
||||
case TextAlign::Right:
|
||||
alignment = DWRITE_TEXT_ALIGNMENT_TRAILING;
|
||||
break;
|
||||
case TextAlign::Center:
|
||||
alignment = DWRITE_TEXT_ALIGNMENT_CENTER;
|
||||
break;
|
||||
case TextAlign::Justified:
|
||||
alignment = DWRITE_TEXT_ALIGNMENT_JUSTIFIED;
|
||||
break;
|
||||
}
|
||||
|
||||
HRESULT hr = text_layout_->SetTextAlignment(alignment);
|
||||
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetTextAlignment failed");
|
||||
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
}
|
||||
#else
|
||||
// not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetWrapWidth(float wrap_width)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
KGE_ASSERT(text_layout_);
|
||||
if (text_layout_)
|
||||
{
|
||||
DWRITE_WORD_WRAPPING wrapping = (wrap_width > 0) ? DWRITE_WORD_WRAPPING_WRAP : DWRITE_WORD_WRAPPING_NO_WRAP;
|
||||
|
||||
HRESULT hr = text_layout_->SetWordWrapping(wrapping);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
if (wrap_width > 0)
|
||||
{
|
||||
hr = text_layout_->SetMaxWidth(wrap_width);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fix the layout width when the text does not wrap
|
||||
DWRITE_TEXT_METRICS metrics;
|
||||
hr = text_layout_->GetMetrics(&metrics);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = text_layout_->SetMaxWidth(metrics.width);
|
||||
}
|
||||
}
|
||||
}
|
||||
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetWordWrapping failed");
|
||||
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
}
|
||||
#else
|
||||
return; // not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextLayout::SetLineSpacing(float line_spacing)
|
||||
{
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
KGE_ASSERT(text_layout_);
|
||||
if (text_layout_)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
float spacing = line_spacing;
|
||||
if (spacing == 0.f)
|
||||
{
|
||||
hr = text_layout_->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_DEFAULT, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = text_layout_->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM, spacing, spacing * 0.8f);
|
||||
}
|
||||
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetLineSpacing failed");
|
||||
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
}
|
||||
KGE_THROW_IF_FAILED(hr, "Apply strikethrough style to text layout failed");
|
||||
#else
|
||||
return; // not supported
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,11 +20,14 @@
|
|||
|
||||
#pragma once
|
||||
#include <kiwano/math/Math.h>
|
||||
#include <kiwano/core/ObjectBase.h>
|
||||
#include <kiwano/render/TextStyle.hpp>
|
||||
|
||||
namespace kiwano
|
||||
{
|
||||
|
||||
KGE_DECLARE_SMART_PTR(TextLayout);
|
||||
|
||||
/**
|
||||
* \addtogroup Render
|
||||
* @{
|
||||
|
|
@ -32,9 +35,19 @@ namespace kiwano
|
|||
|
||||
/// \~chinese
|
||||
/// @brief 文本布局
|
||||
class KGE_API TextLayout : public Noncopyable
|
||||
class KGE_API TextLayout : public virtual ObjectBase
|
||||
{
|
||||
public:
|
||||
/// \~chinese
|
||||
/// @brief 创建文本布局
|
||||
static TextLayoutPtr Create();
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 创建文本布局
|
||||
/// @param content 文字内容
|
||||
/// @param style 文本样式
|
||||
static TextLayoutPtr Create(const String& content, const TextStyle& style);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 构造空的文本布局
|
||||
TextLayout();
|
||||
|
|
@ -48,17 +61,14 @@ public:
|
|||
bool IsDirty() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 更新文本布局
|
||||
/// @note 文本布局是懒更新的,在修改文本布局的属性后需要手动更新
|
||||
void Update();
|
||||
/// @brief 清空文本布局
|
||||
void Clear();
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取文本
|
||||
const String& GetText() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取文本样式
|
||||
const TextStyle& GetStyle() const;
|
||||
/// @brief 重设文本布局
|
||||
/// @param content 文字内容
|
||||
/// @param style 文本样式
|
||||
void Reset(const String& content, const TextStyle& style);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取文本行数
|
||||
|
|
@ -69,44 +79,99 @@ public:
|
|||
Size GetLayoutSize() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取填充画刷
|
||||
BrushPtr GetFillBrush() const;
|
||||
/// @brief 获取默认填充画刷
|
||||
BrushPtr GetDefaultFillBrush() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取描边画刷
|
||||
BrushPtr GetOutlineBrush() const;
|
||||
/// @brief 获取默认描边画刷
|
||||
BrushPtr GetDefaultOutlineBrush() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文本
|
||||
void SetText(const String& text);
|
||||
/// @brief 获取默认描边宽度
|
||||
float GetDefaultOutlineWidth() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文本样式
|
||||
void SetStyle(const TextStyle& style);
|
||||
/// @brief 获取默认描边线条样式
|
||||
StrokeStylePtr GetDefaultOutlineStrokeStyle() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 文字范围
|
||||
struct TextRange
|
||||
{
|
||||
uint32_t start; ///< 起始位置
|
||||
uint32_t length; ///< 长度
|
||||
};
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置字体
|
||||
void SetFont(FontPtr font);
|
||||
/// @param font 字体
|
||||
/// @param range 文字范围
|
||||
void SetFont(FontPtr font, TextRange range);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置字体族
|
||||
void SetFontFamily(String const& family);
|
||||
/// @param family 字体族
|
||||
/// @param range 文字范围
|
||||
void SetFontFamily(String const& family, TextRange range);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置字号(默认值为 18)
|
||||
void SetFontSize(float size);
|
||||
/// @param size 字号
|
||||
/// @param range 文字范围
|
||||
void SetFontSize(float size, TextRange range);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置字体粗细值(默认值为 FontWeight::Normal)
|
||||
void SetFontWeight(uint32_t weight);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文字填充画刷
|
||||
void SetFillBrush(BrushPtr brush);
|
||||
/// @param weight 粗细值
|
||||
/// @param range 文字范围
|
||||
void SetFontWeight(uint32_t weight, TextRange range);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文字斜体(默认值为 false)
|
||||
void SetItalic(bool italic);
|
||||
/// @param italic 是否是斜体
|
||||
/// @param range 文字范围
|
||||
void SetItalic(bool italic, TextRange range);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置下划线
|
||||
/// @param enable 是否显示下划线
|
||||
/// @param range 文字范围
|
||||
void SetUnderline(bool enable, TextRange range);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置删除线
|
||||
/// @param enable 是否显示删除线
|
||||
/// @param range 文字范围
|
||||
void SetStrikethrough(bool enable, TextRange range);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文字填充画刷,描边画刷和描边线宽
|
||||
/// @param brush 画刷
|
||||
/// @param range 文字范围
|
||||
void SetFillBrush(BrushPtr brush, TextRange range);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文字描边画刷
|
||||
/// @param brush 画刷
|
||||
/// @param range 文字范围
|
||||
void SetOutlineBrush(BrushPtr brush, TextRange range);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文字描边线宽
|
||||
/// @param width 描边线宽
|
||||
/// @param range 文字范围
|
||||
void SetOutlineWidth(float width, TextRange range);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置描边线条样式
|
||||
/// @param stroke 线条样式
|
||||
/// @param range 文字范围
|
||||
void SetOutlineStrokeStyle(StrokeStylePtr stroke, TextRange range);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置对齐方式
|
||||
/// @param align 对齐方式
|
||||
void SetAlignment(TextAlign align);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文本自动换行的宽度
|
||||
|
|
@ -117,66 +182,52 @@ public:
|
|||
void SetLineSpacing(float line_spacing);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置对齐方式
|
||||
void SetAlignment(TextAlign align);
|
||||
/// @brief 设置默认文字填充画刷
|
||||
/// @param brush 画刷
|
||||
void SetDefaultFillBrush(BrushPtr brush);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文字描边画刷
|
||||
void SetOutlineBrush(BrushPtr brush);
|
||||
/// @brief 设置默认文字描边画刷
|
||||
/// @param brush 画刷
|
||||
void SetDefaultOutlineBrush(BrushPtr brush);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文字描边线宽
|
||||
void SetOutlineWidth(float outline_width);
|
||||
/// @brief 设置默认文字描边线宽
|
||||
/// @param width 描边线宽
|
||||
void SetDefaultOutlineWidth(float width);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置文字描边线相交样式
|
||||
void SetOutlineStroke(StrokeStylePtr outline_stroke);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置下划线
|
||||
/// @param enable 是否显示下划线
|
||||
/// @param start 起始位置
|
||||
/// @param length 长度
|
||||
void SetUnderline(bool enable, uint32_t start, uint32_t length);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 设置删除线
|
||||
/// @param enable 是否显示删除线
|
||||
/// @param start 起始位置
|
||||
/// @param length 长度
|
||||
void SetStrikethrough(bool enable, uint32_t start, uint32_t length);
|
||||
/// @brief 设置默认描边线条样式
|
||||
/// @param stroke 线条样式
|
||||
void SetDefaultOutlineStrokeStyle(StrokeStylePtr stroke);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 脏数据标志
|
||||
enum DirtyFlag : uint8_t
|
||||
enum class DirtyFlag : uint8_t
|
||||
{
|
||||
Clean = 0, ///< 干净数据
|
||||
DirtyFormat = 1, ///< 文字格式待更新
|
||||
DirtyLayout = 1 << 1, ///< 文字布局待更新
|
||||
Updated = 1 << 2, ///< 数据已更新
|
||||
Clean = 0, ///< 干净布局
|
||||
Dirty = 1 << 0, ///< 脏布局
|
||||
Updated = 1 << 1, ///< 已更新
|
||||
};
|
||||
|
||||
uint8_t GetDirtyFlag() const;
|
||||
DirtyFlag GetDirtyFlag() const;
|
||||
|
||||
void SetDirtyFlag(uint8_t flag);
|
||||
void SetDirtyFlag(DirtyFlag flag);
|
||||
|
||||
private:
|
||||
uint8_t dirty_flag_;
|
||||
String text_;
|
||||
TextStyle style_;
|
||||
DirtyFlag dirty_flag_;
|
||||
BrushPtr default_fill_brush_;
|
||||
BrushPtr default_outline_brush_;
|
||||
float default_outline_width_;
|
||||
StrokeStylePtr default_outline_stroke_;
|
||||
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
public:
|
||||
ComPtr<IDWriteTextFormat> GetTextFormat() const;
|
||||
|
||||
void SetTextFormat(ComPtr<IDWriteTextFormat> format);
|
||||
|
||||
ComPtr<IDWriteTextLayout> GetTextLayout() const;
|
||||
|
||||
void SetTextLayout(ComPtr<IDWriteTextLayout> layout);
|
||||
|
||||
private:
|
||||
ComPtr<IDWriteTextFormat> text_format_;
|
||||
ComPtr<IDWriteTextLayout> text_layout_;
|
||||
#endif
|
||||
};
|
||||
|
|
@ -197,72 +248,72 @@ inline bool TextLayout::IsDirty() const
|
|||
return dirty_flag_ != DirtyFlag::Clean;
|
||||
}
|
||||
|
||||
inline const String& TextLayout::GetText() const
|
||||
inline void TextLayout::Clear()
|
||||
{
|
||||
return text_;
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
text_layout_ = nullptr;
|
||||
dirty_flag_ = DirtyFlag::Updated;
|
||||
#else
|
||||
return; // not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
inline const TextStyle& TextLayout::GetStyle() const
|
||||
{
|
||||
return style_;
|
||||
}
|
||||
|
||||
inline uint8_t TextLayout::GetDirtyFlag() const
|
||||
inline TextLayout::DirtyFlag TextLayout::GetDirtyFlag() const
|
||||
{
|
||||
return dirty_flag_;
|
||||
}
|
||||
|
||||
inline void TextLayout::SetDirtyFlag(uint8_t flag)
|
||||
inline void TextLayout::SetDirtyFlag(TextLayout::DirtyFlag flag)
|
||||
{
|
||||
dirty_flag_ = flag;
|
||||
}
|
||||
|
||||
inline BrushPtr TextLayout::GetFillBrush() const
|
||||
inline BrushPtr TextLayout::GetDefaultFillBrush() const
|
||||
{
|
||||
return style_.fill_brush;
|
||||
return default_fill_brush_;
|
||||
}
|
||||
|
||||
inline BrushPtr TextLayout::GetOutlineBrush() const
|
||||
inline BrushPtr TextLayout::GetDefaultOutlineBrush() const
|
||||
{
|
||||
return style_.outline_brush;
|
||||
return default_outline_brush_;
|
||||
}
|
||||
|
||||
inline void TextLayout::SetFillBrush(BrushPtr brush)
|
||||
inline float TextLayout::GetDefaultOutlineWidth() const
|
||||
{
|
||||
style_.fill_brush = brush;
|
||||
return default_outline_width_;
|
||||
}
|
||||
|
||||
inline void TextLayout::SetOutlineBrush(BrushPtr brush)
|
||||
inline StrokeStylePtr TextLayout::GetDefaultOutlineStrokeStyle() const
|
||||
{
|
||||
style_.outline_brush = brush;
|
||||
return default_outline_stroke_;
|
||||
}
|
||||
|
||||
inline void TextLayout::SetOutlineWidth(float outline_width)
|
||||
inline void TextLayout::SetDefaultFillBrush(BrushPtr brush)
|
||||
{
|
||||
style_.outline_width = outline_width;
|
||||
default_fill_brush_ = brush;
|
||||
}
|
||||
|
||||
inline void TextLayout::SetOutlineStroke(StrokeStylePtr outline_stroke)
|
||||
inline void TextLayout::SetDefaultOutlineBrush(BrushPtr brush)
|
||||
{
|
||||
style_.outline_stroke = outline_stroke;
|
||||
default_outline_brush_ = brush;
|
||||
}
|
||||
|
||||
inline void TextLayout::SetDefaultOutlineWidth(float width)
|
||||
{
|
||||
default_outline_width_ = width;
|
||||
}
|
||||
|
||||
inline void TextLayout::SetDefaultOutlineStrokeStyle(StrokeStylePtr stroke)
|
||||
{
|
||||
default_outline_stroke_ = stroke;
|
||||
}
|
||||
|
||||
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
|
||||
inline ComPtr<IDWriteTextFormat> TextLayout::GetTextFormat() const
|
||||
{
|
||||
return text_format_;
|
||||
}
|
||||
|
||||
inline ComPtr<IDWriteTextLayout> TextLayout::GetTextLayout() const
|
||||
{
|
||||
return text_layout_;
|
||||
}
|
||||
|
||||
inline void TextLayout::SetTextFormat(ComPtr<IDWriteTextFormat> format)
|
||||
{
|
||||
text_format_ = format;
|
||||
}
|
||||
|
||||
inline void TextLayout::SetTextLayout(ComPtr<IDWriteTextLayout> layout)
|
||||
{
|
||||
text_layout_ = layout;
|
||||
|
|
|
|||
|
|
@ -37,9 +37,10 @@ namespace kiwano
|
|||
*/
|
||||
enum class TextAlign
|
||||
{
|
||||
Left, ///< 左对齐
|
||||
Right, ///< 右对齐
|
||||
Center ///< 居中对齐
|
||||
Left, ///< 左对齐
|
||||
Right, ///< 右对齐
|
||||
Center, ///< 居中对齐
|
||||
Justified ///< 两端对齐
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -69,18 +70,20 @@ struct FontWeight
|
|||
class KGE_API TextStyle
|
||||
{
|
||||
public:
|
||||
FontPtr font; ///< 字体
|
||||
String font_family; ///< 字体族
|
||||
float font_size; ///< 字号
|
||||
uint32_t font_weight; ///< 粗细值
|
||||
bool italic; ///< 是否斜体
|
||||
TextAlign alignment; ///< 对齐方式
|
||||
float wrap_width; ///< 自动换行宽度
|
||||
float line_spacing; ///< 行间距
|
||||
BrushPtr fill_brush; ///< 填充画刷
|
||||
BrushPtr outline_brush; ///< 描边画刷
|
||||
float outline_width; ///< 描边线宽
|
||||
StrokeStylePtr outline_stroke; ///< 描边线样式
|
||||
FontPtr font; ///< 字体
|
||||
String font_family; ///< 字体族
|
||||
float font_size; ///< 字号
|
||||
uint32_t font_weight; ///< 粗细值
|
||||
bool italic; ///< 是否斜体
|
||||
TextAlign alignment; ///< 对齐方式
|
||||
BrushPtr fill_brush; ///< 填充画刷
|
||||
BrushPtr outline_brush; ///< 描边画刷
|
||||
float outline_width; ///< 描边线宽
|
||||
StrokeStylePtr outline_stroke; ///< 描边线宽
|
||||
float wrap_width; ///< 自动换行宽度
|
||||
float line_spacing; ///< 行间距
|
||||
bool show_underline; ///< 显示下划线
|
||||
bool show_strikethrough; ///< 显示删除线
|
||||
|
||||
public:
|
||||
/**
|
||||
|
|
@ -113,10 +116,11 @@ inline TextStyle::TextStyle(const String& font_family, float font_size, uint32_t
|
|||
, font_weight(font_weight)
|
||||
, italic(false)
|
||||
, alignment(TextAlign::Left)
|
||||
, outline_width(1.0f)
|
||||
, wrap_width(0)
|
||||
, line_spacing(0)
|
||||
, outline_width(1.0f)
|
||||
, outline_stroke()
|
||||
, show_underline(false)
|
||||
, show_strikethrough(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue