Skip to content

Reshape text at runtime via ViewModel and animation without mutating the document#3595

Open
Hparty wants to merge 33 commits into
mainfrom
feature/partyhuang_text_reshape
Open

Reshape text at runtime via ViewModel and animation without mutating the document#3595
Hparty wants to merge 33 commits into
mainfrom
feature/partyhuang_text_reshape

Conversation

@Hparty

@Hparty Hparty commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

本 PR 让 ViewModel / Animation 在运行时修改文字排版属性(text / fontFamily / fontStyle / fontSize / letterSpacing / fauxBold / fauxItalic)时不改动文档,只重新构造 runtime 对象。

主要内容:

  • 提取 TextGlyphParams 描述单个 Text 的排版输入,与 Text 节点解耦;TextLayout 直接基于 TextElement({Text*, TextGlyphParams})排版。
  • 新增 TextHolder:在 draw 时按当前 glyph 参数 reshape,通过 tgfx::Text::setTextBlob 就地替换 blob,保持 render tree 引用不变,x/y 等运行时位置得以保留。
  • 将文字排版 channel 从 Layout 改为 AnimLayout,使 ViewModel / Animation 能驱动;x / y 仍走直接 writer。
  • 拆分 LayerBuilderContext 为静态基类与 RuntimeLayerBuilderContext 子类(虚拟 hook),去掉 _needsRuntimeData 布尔标志;TextBox convert context 收口为子类成员。
  • 修复 UAF:binding 移除 node 时清理 TextHolder entry。
  • 修复脏检查 gate:PAGScene::hasContentChanged 额外检查 hasDirtyTextHolders,避免延迟 reshape 在 flush 前被跳帧。
  • 优化:TextHolder::apply 比较新值与当前值,只有真正变化才标脏,避免 Hold 插值动画每帧空转 reshape。
  • DEPS 指向 tgfx 已合并的 setTextBlob commit(48a30db3)。
  • 新增 resources/text_reshape_demo.pagx 演示文件与 reshape 测试用例(含 baseline)。

依赖的 tgfx PR(已合并):setTextBlob 接口。

@Hparty
Hparty force-pushed the feature/partyhuang_text_reshape branch from cd39fe5 to c0dc88b Compare July 20, 2026 07:18
@codecov-commenter

codecov-commenter commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.27119% with 182 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.71%. Comparing base (d93adbb) to head (08978b2).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
src/renderer/TextHolder.cpp 37.79% 67 Missing and 12 partials ⚠️
src/cli/CommandVerify.cpp 26.66% 37 Missing and 18 partials ⚠️
src/renderer/LayerBuilder.cpp 82.79% 7 Missing and 9 partials ⚠️
src/pagx/PAGScene.cpp 47.61% 8 Missing and 3 partials ⚠️
src/renderer/TextHolder.h 25.00% 9 Missing ⚠️
src/pagx/TextLayout.cpp 89.09% 2 Missing and 4 partials ⚠️
src/renderer/LayerBuilder.h 75.00% 0 Missing and 2 partials ⚠️
test/src/PAGXTest.cpp 97.26% 0 Missing and 2 partials ⚠️
src/pagx/TextLayoutParams.h 0.00% 0 Missing and 1 partial ⚠️
src/pagx/runtime/PAGComposition.cpp 87.50% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3595      +/-   ##
==========================================
+ Coverage   82.56%   82.71%   +0.14%     
==========================================
  Files         707      710       +3     
  Lines       91382    93468    +2086     
  Branches    25871    26341     +470     
==========================================
+ Hits        75450    77308    +1858     
- Misses      10438    10553     +115     
- Partials     5494     5607     +113     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/pagx/PAGScene.cpp Outdated
// A pending TextHolder reshape (ViewModel/Animation drove a text-shaping channel) has not yet
// touched the tgfx objects — flush runs inside Record(). Without this check the dirty gate would
// skip the frame and the reshape would never reach the screen.
if (_rootComposition != nullptr && _rootComposition->hasDirtyTextHolders()) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

性能:hasContentChanged 每帧调用,此处 hasDirtyTextHolders() 会经 CollectChildCompositions 递归遍历整棵 layer 树;flushTextHolders 每帧再遍历一次。即使文档完全不含 TextHolder(绝大多数存量场景)也照付两次 O(n) 遍历 + 临时 vector 分配。建议在 binding/scene 维护"是否存在任一 TextHolder"的标志做提前返回,或缓存子 composition 列表。

Comment thread src/pagx/runtime/PAGComposition.cpp Outdated
return true;
}
std::vector<PAGComposition*> childComps = {};
CollectChildCompositions(const_cast<PAGComposition*>(this), childComps);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

规范:const 方法内使用 const_cast<PAGComposition*>(this) 违反项目编码规范(应避免此类强制转换)。此处仅为读取而强转,目的是绕过 CollectChildCompositions 的非 const 形参。建议给 CollectChildCompositions 增加 const 重载,或将形参改为 const PAGLayer*,从而去掉 const_cast。

Comment thread spec/pagx.xsd
<xs:attribute name="height" type="xs:decimal" use="required"/>
</xs:complexType>

<!-- ========================= ViewModel & DataBind ========================= -->

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

范围建议:此处新增的 ViewModel/DataBind/Animation/StateMachine 完整 schema(约 +290 行)连同 CommandVerify.cpp 的引用收集(+126 行),与本 PR 主题"文字运行时 reshape"关联较弱,且逻辑独立、可单独合入。建议评估是否拆为独立 PR,以减小评审面积、便于后续回溯。(流程建议,非代码缺陷)

Hparty added 22 commits July 21, 2026 19:55
…writers for ViewModel and animation driven reshaping.
@Hparty
Hparty force-pushed the feature/partyhuang_text_reshape branch from 08978b2 to 78a5ba8 Compare July 21, 2026 12:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants