Fix incorrect TextLayout width

This commit is contained in:
Nomango 2020-02-17 12:06:29 +08:00
parent 60899a6a9b
commit 794df63a10
10 changed files with 238 additions and 169 deletions

View File

@ -80,7 +80,7 @@ FixturePtr Fixture::CreateRect(Body* body, Param const& param, Size const& size,
b2Vec2 b2offset = world->Stage2World(offset); b2Vec2 b2offset = world->Stage2World(offset);
b2PolygonShape shape; b2PolygonShape shape;
shape.SetAsBox(b2size.x / 2, b2size.y / 2, b2offset, rotation); shape.SetAsBox(b2size.x / 2, b2size.y / 2, b2offset, math::Degree2Radian(rotation));
return CreateFixture(body, &shape, param); return CreateFixture(body, &shape, param);
} }

View File

@ -242,10 +242,23 @@ ContactList World::GetContactList()
void World::Update(Duration dt) void World::Update(Duration dt)
{ {
world_.Step(dt.Seconds(), vel_iter_, pos_iter_);
b2Body* b2body = world_.GetBodyList(); b2Body* b2body = world_.GetBodyList();
while (b2body) while (b2body)
{
Body* body = static_cast<Body*>(b2body->GetUserData());
if (body && body->GetType() != Body::Type::Static)
{
body->UpdateFromActor();
}
b2body = b2body->GetNext();
}
world_.Step(dt.Seconds(), vel_iter_, pos_iter_);
b2body = world_.GetBodyList();
while (b2body)
{ {
Body* body = static_cast<Body*>(b2body->GetUserData()); Body* body = static_cast<Body*>(b2body->GetUserData());
if (body && body->GetType() != Body::Type::Static) if (body && body->GetType() != Body::Type::Static)

View File

@ -260,13 +260,13 @@ void Actor::HandleEvent(Event* evt)
} }
} }
Matrix3x2 const& Actor::GetTransformMatrix() const const Matrix3x2& Actor::GetTransformMatrix() const
{ {
UpdateTransform(); UpdateTransform();
return transform_matrix_; return transform_matrix_;
} }
Matrix3x2 const& Actor::GetTransformInverseMatrix() const const Matrix3x2& Actor::GetTransformInverseMatrix() const
{ {
UpdateTransform(); UpdateTransform();
if (dirty_transform_inverse_) if (dirty_transform_inverse_)

View File

@ -114,7 +114,7 @@ public:
/// \~chinese /// \~chinese
/// @brief 获取坐标 /// @brief 获取坐标
Point const& GetPosition() const; virtual Point GetPosition() const;
/// \~chinese /// \~chinese
/// @brief 获取 x 坐标 /// @brief 获取 x 坐标
@ -124,6 +124,10 @@ public:
/// @brief 获取 y 坐标 /// @brief 获取 y 坐标
float GetPositionY() const; float GetPositionY() const;
/// \~chinese
/// @brief 获取大小
virtual Size GetSize() const;
/// \~chinese /// \~chinese
/// @brief 获取宽度 /// @brief 获取宽度
float GetWidth() const; float GetWidth() const;
@ -132,10 +136,6 @@ public:
/// @brief 获取高度 /// @brief 获取高度
float GetHeight() const; float GetHeight() const;
/// \~chinese
/// @brief 获取大小
Size const& GetSize() const;
/// \~chinese /// \~chinese
/// @brief 获取缩放后的宽度 /// @brief 获取缩放后的宽度
float GetScaledWidth() const; float GetScaledWidth() const;
@ -150,7 +150,7 @@ public:
/// \~chinese /// \~chinese
/// @brief 获取锚点 /// @brief 获取锚点
Point const& GetAnchor() const; virtual Point GetAnchor() const;
/// \~chinese /// \~chinese
/// @brief 获取 x 方向锚点 /// @brief 获取 x 方向锚点
@ -162,7 +162,7 @@ public:
/// \~chinese /// \~chinese
/// @brief 获取透明度 /// @brief 获取透明度
float GetOpacity() const; virtual float GetOpacity() const;
/// \~chinese /// \~chinese
/// @brief 获取显示透明度 /// @brief 获取显示透明度
@ -170,11 +170,11 @@ public:
/// \~chinese /// \~chinese
/// @brief 获取旋转角度 /// @brief 获取旋转角度
float GetRotation() const; virtual float GetRotation() const;
/// \~chinese /// \~chinese
/// @brief 获取缩放比例 /// @brief 获取缩放比例
Point const& GetScale() const; virtual Point GetScale() const;
/// \~chinese /// \~chinese
/// @brief 获取横向缩放比例 /// @brief 获取横向缩放比例
@ -186,7 +186,7 @@ public:
/// \~chinese /// \~chinese
/// @brief 获取错切角度 /// @brief 获取错切角度
Point const& GetSkew() const; virtual Point GetSkew() const;
/// \~chinese /// \~chinese
/// @brief 获取横向错切角度 /// @brief 获取横向错切角度
@ -218,11 +218,11 @@ public:
/// \~chinese /// \~chinese
/// @brief 获取二维变换矩阵 /// @brief 获取二维变换矩阵
Matrix3x2 const& GetTransformMatrix() const; const Matrix3x2& GetTransformMatrix() const;
/// \~chinese /// \~chinese
/// @brief 获取二维变换的逆矩阵 /// @brief 获取二维变换的逆矩阵
Matrix3x2 const& GetTransformInverseMatrix() const; const Matrix3x2& GetTransformInverseMatrix() const;
/// \~chinese /// \~chinese
/// @brief 设置角色是否可见 /// @brief 设置角色是否可见
@ -284,14 +284,6 @@ public:
/// @brief 设置锚点位置,默认为 (0, 0), 范围 [0, 1] /// @brief 设置锚点位置,默认为 (0, 0), 范围 [0, 1]
void SetAnchor(float anchorx, float anchory); void SetAnchor(float anchorx, float anchory);
/// \~chinese
/// @brief 修改宽度
virtual void SetWidth(float width);
/// \~chinese
/// @brief 修改高度
virtual void SetHeight(float height);
/// \~chinese /// \~chinese
/// @brief 修改大小 /// @brief 修改大小
virtual void SetSize(Size const& size); virtual void SetSize(Size const& size);
@ -300,6 +292,14 @@ public:
/// @brief 修改大小 /// @brief 修改大小
void SetSize(float width, float height); void SetSize(float width, float height);
/// \~chinese
/// @brief 修改宽度
void SetWidth(float width);
/// \~chinese
/// @brief 修改高度
void SetHeight(float height);
/// \~chinese /// \~chinese
/// @brief 设置透明度,默认为 1.0, 范围 [0, 1] /// @brief 设置透明度,默认为 1.0, 范围 [0, 1]
virtual void SetOpacity(float opacity); virtual void SetOpacity(float opacity);
@ -514,7 +514,7 @@ inline int Actor::GetZOrder() const
return z_order_; return z_order_;
} }
inline Point const& Actor::GetPosition() const inline Point Actor::GetPosition() const
{ {
return transform_.position; return transform_.position;
} }
@ -529,7 +529,7 @@ inline float Actor::GetPositionY() const
return GetPosition().y; return GetPosition().y;
} }
inline Point const& Actor::GetScale() const inline Point Actor::GetScale() const
{ {
return transform_.scale; return transform_.scale;
} }
@ -544,7 +544,7 @@ inline float Actor::GetScaleY() const
return GetScale().y; return GetScale().y;
} }
inline Point const& Actor::GetSkew() const inline Point Actor::GetSkew() const
{ {
return transform_.skew; return transform_.skew;
} }
@ -574,7 +574,7 @@ inline float Actor::GetHeight() const
return GetSize().y; return GetSize().y;
} }
inline Size const& Actor::GetSize() const inline Size Actor::GetSize() const
{ {
return size_; return size_;
} }
@ -594,7 +594,7 @@ inline Size Actor::GetScaledSize() const
return Size{ GetScaledWidth(), GetScaledHeight() }; return Size{ GetScaledWidth(), GetScaledHeight() };
} }
inline Point const& Actor::GetAnchor() const inline Point Actor::GetAnchor() const
{ {
return anchor_; return anchor_;
} }
@ -693,4 +693,5 @@ inline void Actor::SetSkew(float skewx, float skewy)
{ {
SetSkew(Vec2{ skewx, skewy }); SetSkew(Vec2{ skewx, skewy });
} }
} // namespace kiwano } // namespace kiwano

View File

@ -124,7 +124,7 @@ void DebugActor::OnUpdate(Duration dt)
debug_text_.Reset(ss.str(), debug_text_style_); debug_text_.Reset(ss.str(), debug_text_style_);
Size layout_size = debug_text_.GetLayoutSize(); Size layout_size = debug_text_.GetSize();
if (layout_size.x > GetWidth() - 20) if (layout_size.x > GetWidth() - 20)
{ {
SetWidth(20 + layout_size.x); SetWidth(20 + layout_size.x);

View File

@ -61,34 +61,10 @@ void TextActor::OnRender(RenderContext& ctx)
} }
} }
void TextActor::OnUpdate(Duration dt) Size TextActor::GetSize() const
{ {
UpdateLayout(); const_cast<TextActor*>(this)->UpdateDirtyLayout();
} return Actor::GetSize();
void TextActor::UpdateLayout()
{
if (!layout_)
return;
if (layout_->GetDirtyFlag() == TextLayout::DirtyFlag::Updated)
{
layout_->SetDirtyFlag(TextLayout::DirtyFlag::Clean);
if (text_.empty())
{
SetSize(Size());
}
else
{
SetSize(layout_->GetLayoutSize());
}
}
}
bool TextActor::CheckVisibility(RenderContext& ctx) const
{
return layout_ && layout_->IsValid() && Actor::CheckVisibility(ctx);
} }
void TextActor::SetFillColor(Color const& color) void TextActor::SetFillColor(Color const& color)
@ -124,7 +100,7 @@ void TextActor::SetTextLayout(TextLayoutPtr layout)
if (layout_ != layout) if (layout_ != layout)
{ {
layout_ = layout; layout_ = layout;
UpdateLayout(); ForceUpdateLayout();
} }
} }
@ -294,4 +270,39 @@ void TextActor::SetOutlineStrokeStyle(StrokeStylePtr stroke)
} }
} }
void TextActor::Update(Duration dt)
{
this->UpdateDirtyLayout();
Actor::Update(dt);
}
bool TextActor::CheckVisibility(RenderContext& ctx) const
{
return layout_ && layout_->IsValid() && Actor::CheckVisibility(ctx);
}
void TextActor::UpdateDirtyLayout()
{
KGE_ASSERT(layout_);
if (layout_->UpdateWhenDirty())
{
ForceUpdateLayout();
}
}
void TextActor::ForceUpdateLayout()
{
KGE_ASSERT(layout_);
layout_->UpdateWhenDirty();
if (text_.empty())
{
SetSize(Size());
}
else
{
SetSize(layout_->GetSize());
}
}
} // namespace kiwano } // namespace kiwano

View File

@ -67,8 +67,8 @@ public:
TextLayoutPtr GetLayout() const; TextLayoutPtr GetLayout() const;
/// \~chinese /// \~chinese
/// @brief 获取文本布局大小 /// @brief 获取大小
Size GetLayoutSize() const; Size GetSize() const override;
/// \~chinese /// \~chinese
/// @brief 获取填充画刷 /// @brief 获取填充画刷
@ -159,15 +159,20 @@ public:
void SetTextLayout(TextLayoutPtr layout); void SetTextLayout(TextLayoutPtr layout);
/// \~chinese /// \~chinese
/// @brief 更新文字布局 /// @brief 更新脏文字布局
/// @details 仅当文字布局脏时更新
void UpdateDirtyLayout();
/// \~chinese
/// @brief 强制更新文字布局
/// @details 文字布局是懒更新的,手动更新文字布局以更新节点状态 /// @details 文字布局是懒更新的,手动更新文字布局以更新节点状态
void UpdateLayout(); void ForceUpdateLayout();
void OnRender(RenderContext& ctx) override; void OnRender(RenderContext& ctx) override;
void OnUpdate(Duration dt) override;
protected: protected:
void Update(Duration dt) override;
bool CheckVisibility(RenderContext& ctx) const override; bool CheckVisibility(RenderContext& ctx) const override;
private: private:
@ -198,15 +203,6 @@ inline TextLayoutPtr TextActor::GetLayout() const
return layout_; return layout_;
} }
inline Size TextActor::GetLayoutSize() const
{
if (layout_)
{
return layout_->GetLayoutSize();
}
return Size();
}
inline BrushPtr TextActor::GetFillBrush() const inline BrushPtr TextActor::GetFillBrush() const
{ {
return style_.fill_brush; return style_.fill_brush;

View File

@ -610,7 +610,7 @@ void RendererImpl::CreateTextLayout(TextLayout& layout, const String& content, c
if (content.empty()) if (content.empty())
{ {
layout.Clear(); layout.Clear();
layout.SetDirtyFlag(TextLayout::DirtyFlag::Updated); layout.SetDirtyFlag(TextLayout::DirtyFlag::Dirty);
return; return;
} }
@ -635,7 +635,7 @@ void RendererImpl::CreateTextLayout(TextLayout& layout, const String& content, c
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
NativePtr::Set(layout, output); NativePtr::Set(layout, output);
layout.SetDirtyFlag(TextLayout::DirtyFlag::Updated); layout.SetDirtyFlag(TextLayout::DirtyFlag::Dirty);
} }
} }
} }

View File

@ -45,14 +45,15 @@ TextLayoutPtr TextLayout::Create(const String& content, const TextStyle& style)
TextLayout::TextLayout() TextLayout::TextLayout()
: dirty_flag_(DirtyFlag::Clean) : dirty_flag_(DirtyFlag::Clean)
, line_count_(0)
{ {
} }
void TextLayout::Reset(const String& text, const TextStyle& style) void TextLayout::Reset(const String& content, const TextStyle& style)
{ {
if (!text.empty()) if (!content.empty())
{ {
Renderer::GetInstance().CreateTextLayout(*this, text, style); Renderer::GetInstance().CreateTextLayout(*this, content, style);
SetAlignment(style.alignment); SetAlignment(style.alignment);
SetWrapWidth(style.wrap_width); SetWrapWidth(style.wrap_width);
@ -63,80 +64,58 @@ void TextLayout::Reset(const String& text, const TextStyle& style)
SetDefaultOutlineStrokeStyle(style.outline_stroke); SetDefaultOutlineStrokeStyle(style.outline_stroke);
if (style.show_underline) if (style.show_underline)
SetUnderline(style.show_underline, { 0, text.length() }); SetUnderline(style.show_underline, { 0, content.length() });
if (style.show_strikethrough) if (style.show_strikethrough)
SetStrikethrough(style.show_strikethrough, { 0, text.length() }); SetStrikethrough(style.show_strikethrough, { 0, content.length() });
} }
else else
{ {
Clear(); Clear();
} }
content_ = content;
SetDirtyFlag(DirtyFlag::Dirty);
}
Size TextLayout::GetSize() const
{
const_cast<TextLayout*>(this)->UpdateWhenDirty();
return size_;
} }
uint32_t TextLayout::GetLineCount() const uint32_t TextLayout::GetLineCount() const
{ {
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX const_cast<TextLayout*>(this)->UpdateWhenDirty();
auto native = NativePtr::Get<IDWriteTextLayout>(this); return line_count_;
KGE_ASSERT(native);
if (native)
{
DWRITE_TEXT_METRICS metrics;
if (SUCCEEDED(native->GetMetrics(&metrics)))
{
return metrics.lineCount;
}
}
#else
// not supported
#endif
return 0;
}
Size TextLayout::GetLayoutSize() const
{
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
auto native = NativePtr::Get<IDWriteTextLayout>(this);
KGE_ASSERT(native);
if (native)
{
DWRITE_TEXT_METRICS metrics;
if (SUCCEEDED(native->GetMetrics(&metrics)))
{
return (metrics.layoutWidth > 0) ? Size(metrics.layoutWidth, metrics.height)
: Size(metrics.width, metrics.height);
}
}
#else
// not supported
#endif
return Size();
} }
void TextLayout::SetFont(FontPtr font, TextRange range) void TextLayout::SetFont(FontPtr font, TextRange range)
{ {
KGE_ASSERT(content_.size() >= (range.start + range.length));
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
auto native = NativePtr::Get<IDWriteTextLayout>(this); auto native = NativePtr::Get<IDWriteTextLayout>(this);
KGE_ASSERT(native); KGE_ASSERT(native);
if (native) if (native)
{ {
ComPtr<IDWriteFontCollection> collection = NativePtr::Get<IDWriteFontCollection>(font); auto collection = NativePtr::Get<IDWriteFontCollection>(font);
HRESULT hr = native->SetFontCollection(collection.Get(), { range.start, range.length }); HRESULT hr = native->SetFontCollection(collection.Get(), { range.start, range.length });
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontCollection failed"); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontCollection failed");
dirty_flag_ = DirtyFlag::Updated;
} }
#else #else
// not supported // not supported
#endif #endif
SetDirtyFlag(DirtyFlag::Dirty);
} }
void TextLayout::SetFontFamily(String const& family, TextRange range) void TextLayout::SetFontFamily(String const& family, TextRange range)
{ {
KGE_ASSERT(content_.size() >= (range.start + range.length));
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
auto native = NativePtr::Get<IDWriteTextLayout>(this); auto native = NativePtr::Get<IDWriteTextLayout>(this);
KGE_ASSERT(native); KGE_ASSERT(native);
@ -147,16 +126,18 @@ void TextLayout::SetFontFamily(String const& family, TextRange range)
HRESULT hr = native->SetFontFamilyName(font_family.c_str(), { range.start, range.length }); HRESULT hr = native->SetFontFamilyName(font_family.c_str(), { range.start, range.length });
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontFamilyName failed"); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontFamilyName failed");
dirty_flag_ = DirtyFlag::Updated;
} }
#else #else
// not supported // not supported
#endif #endif
SetDirtyFlag(DirtyFlag::Dirty);
} }
void TextLayout::SetFontSize(float size, TextRange range) void TextLayout::SetFontSize(float size, TextRange range)
{ {
KGE_ASSERT(content_.size() >= (range.start + range.length));
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
auto native = NativePtr::Get<IDWriteTextLayout>(this); auto native = NativePtr::Get<IDWriteTextLayout>(this);
KGE_ASSERT(native); KGE_ASSERT(native);
@ -165,16 +146,18 @@ void TextLayout::SetFontSize(float size, TextRange range)
{ {
HRESULT hr = native->SetFontSize(size, { range.start, range.length }); HRESULT hr = native->SetFontSize(size, { range.start, range.length });
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontSize failed"); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontSize failed");
dirty_flag_ = DirtyFlag::Updated;
} }
#else #else
// not supported // not supported
#endif #endif
SetDirtyFlag(DirtyFlag::Dirty);
} }
void TextLayout::SetFontWeight(uint32_t weight, TextRange range) void TextLayout::SetFontWeight(uint32_t weight, TextRange range)
{ {
KGE_ASSERT(content_.size() >= (range.start + range.length));
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
auto native = NativePtr::Get<IDWriteTextLayout>(this); auto native = NativePtr::Get<IDWriteTextLayout>(this);
KGE_ASSERT(native); KGE_ASSERT(native);
@ -185,16 +168,18 @@ void TextLayout::SetFontWeight(uint32_t weight, TextRange range)
HRESULT hr = native->SetFontWeight(font_weight, { range.start, range.length }); HRESULT hr = native->SetFontWeight(font_weight, { range.start, range.length });
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontWeight failed"); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontWeight failed");
dirty_flag_ = DirtyFlag::Updated;
} }
#else #else
// not supported // not supported
#endif #endif
SetDirtyFlag(DirtyFlag::Dirty);
} }
void TextLayout::SetItalic(bool italic, TextRange range) void TextLayout::SetItalic(bool italic, TextRange range)
{ {
KGE_ASSERT(content_.size() >= (range.start + range.length));
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
auto native = NativePtr::Get<IDWriteTextLayout>(this); auto native = NativePtr::Get<IDWriteTextLayout>(this);
KGE_ASSERT(native); KGE_ASSERT(native);
@ -205,16 +190,18 @@ void TextLayout::SetItalic(bool italic, TextRange range)
HRESULT hr = native->SetFontStyle(font_style, { range.start, range.length }); HRESULT hr = native->SetFontStyle(font_style, { range.start, range.length });
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontStyle failed"); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontStyle failed");
dirty_flag_ = DirtyFlag::Updated;
} }
#else #else
// not supported // not supported
#endif #endif
SetDirtyFlag(DirtyFlag::Dirty);
} }
void TextLayout::SetUnderline(bool enable, TextRange range) void TextLayout::SetUnderline(bool enable, TextRange range)
{ {
KGE_ASSERT(content_.size() >= (range.start + range.length));
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
auto native = NativePtr::Get<IDWriteTextLayout>(this); auto native = NativePtr::Get<IDWriteTextLayout>(this);
KGE_ASSERT(native); KGE_ASSERT(native);
@ -223,16 +210,18 @@ void TextLayout::SetUnderline(bool enable, TextRange range)
{ {
HRESULT hr = native->SetUnderline(enable, { range.start, range.length }); HRESULT hr = native->SetUnderline(enable, { range.start, range.length });
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetUnderline failed"); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetUnderline failed");
dirty_flag_ = DirtyFlag::Updated;
} }
#else #else
return; // not supported // not supported
#endif #endif
SetDirtyFlag(DirtyFlag::Dirty);
} }
void TextLayout::SetStrikethrough(bool enable, TextRange range) void TextLayout::SetStrikethrough(bool enable, TextRange range)
{ {
KGE_ASSERT(content_.size() >= (range.start + range.length));
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
auto native = NativePtr::Get<IDWriteTextLayout>(this); auto native = NativePtr::Get<IDWriteTextLayout>(this);
KGE_ASSERT(native); KGE_ASSERT(native);
@ -241,16 +230,18 @@ void TextLayout::SetStrikethrough(bool enable, TextRange range)
{ {
HRESULT hr = native->SetStrikethrough(enable, { range.start, range.length }); HRESULT hr = native->SetStrikethrough(enable, { range.start, range.length });
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetStrikethrough failed"); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetStrikethrough failed");
dirty_flag_ = DirtyFlag::Updated;
} }
#else #else
return; // not supported // not supported
#endif #endif
SetDirtyFlag(DirtyFlag::Dirty);
} }
void TextLayout::SetFillBrush(BrushPtr brush, TextRange range) void TextLayout::SetFillBrush(BrushPtr brush, TextRange range)
{ {
KGE_ASSERT(content_.size() >= (range.start + range.length));
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
auto native = NativePtr::Get<IDWriteTextLayout>(this); auto native = NativePtr::Get<IDWriteTextLayout>(this);
KGE_ASSERT(native); KGE_ASSERT(native);
@ -260,16 +251,18 @@ void TextLayout::SetFillBrush(BrushPtr brush, TextRange range)
HRESULT hr = HRESULT hr =
native->SetDrawingEffect(NativePtr::Get<ID2D1Brush>(brush).Get(), { range.start, range.length }); native->SetDrawingEffect(NativePtr::Get<ID2D1Brush>(brush).Get(), { range.start, range.length });
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetDrawingEffect failed"); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetDrawingEffect failed");
dirty_flag_ = DirtyFlag::Updated;
} }
#else #else
return; // not supported // not supported
#endif #endif
SetDirtyFlag(DirtyFlag::Dirty);
} }
void TextLayout::SetOutlineBrush(BrushPtr brush, TextRange range) void TextLayout::SetOutlineBrush(BrushPtr brush, TextRange range)
{ {
KGE_ASSERT(content_.size() >= (range.start + range.length));
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
// TODO // TODO
KGE_NOT_USED(range); KGE_NOT_USED(range);
@ -281,6 +274,8 @@ void TextLayout::SetOutlineBrush(BrushPtr brush, TextRange range)
void TextLayout::SetOutlineStrokeStyle(StrokeStylePtr stroke, TextRange range) void TextLayout::SetOutlineStrokeStyle(StrokeStylePtr stroke, TextRange range)
{ {
KGE_ASSERT(content_.size() >= (range.start + range.length));
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
// TODO // TODO
KGE_NOT_USED(range); KGE_NOT_USED(range);
@ -317,12 +312,12 @@ void TextLayout::SetAlignment(TextAlign align)
HRESULT hr = native->SetTextAlignment(alignment); HRESULT hr = native->SetTextAlignment(alignment);
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetTextAlignment failed"); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetTextAlignment failed");
dirty_flag_ = DirtyFlag::Updated;
} }
#else #else
// not supported // not supported
#endif #endif
SetDirtyFlag(DirtyFlag::Dirty);
} }
void TextLayout::SetWrapWidth(float wrap_width) void TextLayout::SetWrapWidth(float wrap_width)
@ -333,34 +328,26 @@ void TextLayout::SetWrapWidth(float wrap_width)
if (native) if (native)
{ {
DWRITE_WORD_WRAPPING wrapping = (wrap_width > 0) ? DWRITE_WORD_WRAPPING_WRAP : DWRITE_WORD_WRAPPING_NO_WRAP; HRESULT hr = S_OK;
if (wrap_width > 0)
HRESULT hr = native->SetWordWrapping(wrapping);
if (SUCCEEDED(hr))
{ {
if (wrap_width > 0) hr = native->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
if (SUCCEEDED(hr))
{ {
hr = native->SetMaxWidth(wrap_width); hr = native->SetMaxWidth(wrap_width);
} }
else }
{ else
// Fix the layout width when the text does not wrap {
DWRITE_TEXT_METRICS metrics; hr = native->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
hr = native->GetMetrics(&metrics);
if (SUCCEEDED(hr))
{
hr = native->SetMaxWidth(metrics.width);
}
}
} }
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetWordWrapping failed"); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetWordWrapping failed");
dirty_flag_ = DirtyFlag::Updated;
} }
#else #else
return; // not supported // not supported
#endif #endif
SetDirtyFlag(DirtyFlag::Dirty);
} }
void TextLayout::SetLineSpacing(float line_spacing) void TextLayout::SetLineSpacing(float line_spacing)
@ -383,12 +370,62 @@ void TextLayout::SetLineSpacing(float line_spacing)
hr = native->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM, spacing, spacing * 0.8f); hr = native->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM, spacing, spacing * 0.8f);
} }
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetLineSpacing failed"); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetLineSpacing failed");
dirty_flag_ = DirtyFlag::Updated;
} }
#else #else
return; // not supported // not supported
#endif #endif
SetDirtyFlag(DirtyFlag::Dirty);
}
bool TextLayout::UpdateWhenDirty()
{
#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX
if (dirty_flag_ == DirtyFlag::Dirty)
{
SetDirtyFlag(DirtyFlag::Clean);
auto native = NativePtr::Get<IDWriteTextLayout>(this);
KGE_ASSERT(native);
HRESULT hr = S_OK;
DWRITE_TEXT_METRICS metrics;
hr = native->GetMetrics(&metrics);
if (SUCCEEDED(hr))
{
if (native->GetWordWrapping() == DWRITE_WORD_WRAPPING_NO_WRAP)
{
// Fix the layout width when the text does not wrap
hr = native->SetMaxWidth(metrics.widthIncludingTrailingWhitespace);
if (SUCCEEDED(hr))
{
hr = native->GetMetrics(&metrics);
}
}
}
if (SUCCEEDED(hr))
{
line_count_ = metrics.lineCount;
if (metrics.layoutWidth > 0)
{
size_ = Size(metrics.layoutWidth, metrics.height);
}
else
{
size_ = Size(metrics.widthIncludingTrailingWhitespace, metrics.height);
}
}
KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::GetMetrics failed");
return true;
}
#else
// not supported
#endif
return false;
} }
} // namespace kiwano } // namespace kiwano

View File

@ -67,12 +67,12 @@ public:
void Reset(const String& content, const TextStyle& style); void Reset(const String& content, const TextStyle& style);
/// \~chinese /// \~chinese
/// @brief 获取文本行数 /// @brief 获取文本布局大小
uint32_t GetLineCount() const; Size GetSize() const;
/// \~chinese /// \~chinese
/// @brief 获取文本布局大小 /// @brief 获取文本行数
Size GetLayoutSize() const; uint32_t GetLineCount() const;
/// \~chinese /// \~chinese
/// @brief 获取默认填充画刷 /// @brief 获取默认填充画刷
@ -183,23 +183,34 @@ public:
void SetDefaultOutlineStrokeStyle(StrokeStylePtr stroke); void SetDefaultOutlineStrokeStyle(StrokeStylePtr stroke);
/// \~chinese /// \~chinese
/// @brief 脏数据标志 /// @brief 脏布局标志
enum class DirtyFlag : uint8_t enum class DirtyFlag : uint8_t
{ {
Clean = 0, ///< 干净布局 Clean = 0, ///< 干净布局
Dirty = 1 << 0, ///< 脏布局 Dirty = 1 << 0 ///< 脏布局
Updated = 1 << 1, ///< 已更新
}; };
/// \~chinese
/// @brief 获取脏布局标志
DirtyFlag GetDirtyFlag() const; DirtyFlag GetDirtyFlag() const;
/// \~chinese
/// @brief 设置脏布局标志
void SetDirtyFlag(DirtyFlag flag); void SetDirtyFlag(DirtyFlag flag);
/// \~chinese
/// @brief 更新脏布局
/// @return 是否需要更新
bool UpdateWhenDirty();
private: private:
DirtyFlag dirty_flag_; DirtyFlag dirty_flag_;
uint32_t line_count_;
Size size_;
BrushPtr default_fill_brush_; BrushPtr default_fill_brush_;
BrushPtr default_outline_brush_; BrushPtr default_outline_brush_;
StrokeStylePtr default_outline_stroke_; StrokeStylePtr default_outline_stroke_;
String content_;
}; };
/** @} */ /** @} */