Skip to content

Commit 46b0967

Browse files
authored
[2039] 统计信息弹窗用qml重写 (#4040)
1 parent 74766b0 commit 46b0967

13 files changed

Lines changed: 429 additions & 48 deletions

File tree

TeXmacs/progs/texmacs/texmacs/tm-tools.scm

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -139,44 +139,48 @@
139139
(if (selection-active-any?) (selection-tree) (buffer-tree))
140140
) ;define
141141

142-
(tm-widget ((show-counts-widget lines) done)
143-
(resize "600guipx"
144-
"300guipx"
145-
(vlist (hlist (glue #t #f 0 0)
146-
(vlist (glue #f #t 120 6)
147-
(bold (text "Stats information"))
148-
(glue #f #t 120 6)
149-
(for (l lines) === (text l))
150-
) ;vlist
151-
(glue #t #f 0 0)
152-
) ;hlist
153-
(glue #f #t 120 6)
154-
(hlist (glue #t #f 120 3) (bottom-buttons ("Close" (done))))
155-
) ;vlist
156-
) ;resize
157-
) ;tm-widget
158-
159-
(tm-define (show-counts)
160-
(:interactive #t)
161-
(let* ((doc (selection-or-document))
162-
(chars (count-characters doc))
142+
(define (compute-stats-data doc page-str)
143+
(let* ((chars (count-characters doc))
163144
(chars-ns (count-chars-no-space doc))
164145
(lines (count-lines doc))
165146
(p (count-chinese-and-words doc))
166147
(chinese (car p))
167148
(words (count-words doc))
168-
(pages (get-page-count))
169-
(lst (list (string-append "Page count: " (number->string pages))
170-
(string-append "Word count: " (number->string (+ words chinese)))
171-
(string-append "Character count (with spaces): " (number->string chars))
172-
(string-append "Character count (without spaces): " (number->string chars-ns))
173-
(string-append "Paragraph count: " (number->string lines))
174-
(string-append "Non-Chinese word: " (number->string words))
175-
(string-append "Chinese character: " (number->string chinese))
176-
) ;list
177-
) ;lst
178149
) ;
179-
(dialogue-window (show-counts-widget lst) noop "Document statistics")
150+
(list (list (translate "Page count") page-str)
151+
(list (translate "Word count") (number->string (+ words chinese)))
152+
(list (translate "Character count (with spaces)") (number->string chars))
153+
(list (translate "Character count (without spaces)") (number->string chars-ns))
154+
(list (translate "Paragraph count") (number->string lines))
155+
(list (translate "Non-Chinese word") (number->string words))
156+
(list (translate "Chinese character") (number->string chinese))
157+
) ;list
158+
) ;let*
159+
) ;define
160+
161+
(tm-define (get-statistics-data)
162+
(:synopsis "Return ((label value) ...) for current document or selection.")
163+
(let* ((doc (selection-or-document)) (pages (number->string (get-page-count))))
164+
(compute-stats-data doc pages)
165+
) ;let*
166+
) ;tm-define
167+
168+
(tm-define (get-statistics-data-from-doc doc)
169+
(:synopsis "Return ((label value) ...) for given doc tree. Page count is 0.")
170+
(compute-stats-data doc "0")
171+
) ;tm-define
172+
173+
(define (statistics-data->stree data)
174+
;; (stats (item <label> <value>) ...)
175+
(cons 'stats
176+
(map (lambda (pair) (cons 'item (list (car pair) (cadr pair)))) data)
177+
) ;cons
178+
) ;define
179+
180+
(tm-define (show-counts)
181+
(:interactive #t)
182+
(let* ((data (get-statistics-data)) (stree (statistics-data->stree data)))
183+
(cpp-statistics-dialog (translate "Document statistics") (stree->tree stree))
180184
) ;let*
181185
) ;tm-define
182186

TeXmacs/tests/202_53.scm

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,25 @@
1010
;;
1111
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1212

13-
(use-modules
14-
(data tmu)
15-
(texmacs texmacs tm-tools))
13+
(use-modules (data tmu) (texmacs texmacs tm-tools))
1614

17-
(import (liii check)
18-
(liii path))
15+
(import (liii check) (liii path))
1916

2017
(tm-define (test_202_53)
21-
(let* ((test-doc (tmu->texmacs ((path (url->system (get-texmacs-path))) :/ "tests" :/ "tmu" :/ "202_53.tmu" :read-text)))
18+
(let* ((tmu-path "TeXmacs/tests/tmu/202_53.tmu")
19+
(test-doc (tmu->texmacs (path-read-text tmu-path)))
2220
(non-chinese (count-words test-doc))
2321
(chars-no-space (count-chars-no-space test-doc))
2422
(chinese (count-chinese-and-words test-doc))
2523
(line-count (count-lines test-doc))
26-
(char-count (count-characters test-doc)))
27-
28-
;; 测试页数 - 预期: 2
24+
(char-count (count-characters test-doc))
25+
;; 测试 get-statistics-data-from-doc 数据组装
26+
(data (get-statistics-data-from-doc test-doc))
27+
(data-labels (map car data))
28+
(data-values (map cadr data))
29+
) ;
2930

31+
;; --- 计数函数 ---
3032
;; 测试字数 - 预期: 16 (10个中文字符 + 6个非中文单词)
3133
(check (+ (car chinese) non-chinese) => 16)
3234

@@ -40,8 +42,29 @@
4042
(check line-count => 3)
4143

4244
;; 测试非中文单词数 - 预期: 6
43-
(check non-chinese => 6) ; 非中文单词
45+
(check non-chinese => 6)
46+
47+
;; 测试中文字符数 - 预期: 10
48+
(check (car chinese) => 10)
49+
50+
;; --- get-statistics-data-from-doc 数据格式 ---
51+
;; 验证标签顺序与内容(需与 get-statistics-data-from-doc 使用相同的 translate)
52+
(check data-labels
53+
=>
54+
(list (translate "Page count")
55+
(translate "Word count")
56+
(translate "Character count (with spaces)")
57+
(translate "Character count (without spaces)")
58+
(translate "Paragraph count")
59+
(translate "Non-Chinese word")
60+
(translate "Chinese character")
61+
) ;list
62+
) ;check
63+
64+
;; 验证 7 个统计项
65+
(check (length data) => 7)
4466

45-
;; 测试中文单词数 - 预期: 10
46-
(check (car chinese) => 10) ; 中文字符
47-
))
67+
;; 验证各项值
68+
(check data-values => '("0" "16" "28" "23" "3" "6" "10"))
69+
) ;let*
70+
) ;tm-define

ai-docs/qml/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ API 速查(`utils/library/dialog-value-table`,entry-key 由调用方自定
120120
同一 Rectangle 既 `border``clip:true` 会把外侧半像素 border 自身裁掉、几乎不可见
121121
(叠加 `radius` 的抗锯齿偏移更甚)。要带边框的裁剪容器,拆**双层 Rectangle**:外层只画
122122
border(不 clip),内层 `clip:true` 容纳子项。下拉浮层(DialogShell overlay)即此结构。
123+
- **scm 侧传给 QML 的用户可见文本一律走 `(translate "key")`**:标签、标题、按钮文案等
124+
在 scm 构造数据时即用 `translate` 包裹,不要硬编码英文。值(数字、内部 key)不翻译。
125+
字典条目按最小粒度登记(如 `"character count"` / `"with spaces"`),系统自动拼接
126+
`"Character count (with spaces)"``"字符数(计空格)"`),不要为每种组合加整条字典。
127+
- **跨弹窗复用的布局常量收进 `Theme.qml`**:行高、间距等可能被多个弹窗共用的数字,统一加在
128+
`atoms/Theme.qml` 的结构尺寸/间距阶梯段。优先用已有常量组合(如 `Theme.btnH + Theme.padS * 2`
129+
表示按钮区高度),避免为单一用途加新常量。成品弹窗只保留本弹窗专属的布局参数(如列宽比
130+
`labelW`),引用 Theme 常量(`Theme.pad`/`Theme.gapS`/`Theme.inlineGap`/`Theme.textRowH` 等)。
123131
- **跨 parent 链查找的 property 不能喂给 binding**:如 EnumCombo 的 `dialogShell`(沿
124132
parent 链按 objectName 查找)是命令式 property,parent 变化不触发其重算,binding 会在
125133
创建瞬间读到 null 永久卡住。依赖它的几何(comboX/Y/W)须在展开时用 `updateGeometry()`

ai-docs/qml/qml-dialog.html

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,38 @@
258258
margin: 0 4px;
259259
}
260260

261+
.stats-table {
262+
display: flex;
263+
flex-direction: column;
264+
gap: 8px;
265+
}
266+
267+
.stats-row {
268+
display: flex;
269+
justify-content: space-between;
270+
align-items: baseline;
271+
padding: 6px 0;
272+
border-bottom: 1px solid #eef3f3;
273+
}
274+
275+
.stats-row:last-child {
276+
border-bottom: 0;
277+
}
278+
279+
.stats-label {
280+
font-size: 14px;
281+
color: #355059;
282+
}
283+
284+
.stats-value {
285+
font-size: 14px;
286+
font-weight: 700;
287+
color: #1d2629;
288+
font-family: var(--mono);
289+
min-width: 40px;
290+
text-align: right;
291+
}
292+
261293
.form-shell {
262294
width: min(540px, 100%);
263295
background: #ffffff;
@@ -716,6 +748,7 @@ <h1>QML 组件模拟器</h1>
716748
<button class="nav-btn" data-target="form">FormDialog.qml</button>
717749
<button class="nav-btn" data-target="font">FontSelector.qml</button>
718750
<button class="nav-btn" data-target="paragraph">ParagraphFormat.qml</button>
751+
<button class="nav-btn" data-target="statistics">Statistics.qml</button>
719752
<div class="sidebar-foot">模拟目标: DialogShell / DialogButtons / EnumCombo / SelectableList / PreviewPane 组合逻辑。</div>
720753
</aside>
721754

@@ -999,6 +1032,51 @@ <h4>字号</h4>
9991032
Cancel/重置走快照撤销(initial-snapshot + revert-to-snapshot)
10001033
</p>
10011034
</section>
1035+
1036+
<section class="panel" id="panel-statistics">
1037+
<article class="dialog" style="max-width: 380px;">
1038+
<h3>Document statistics</h3>
1039+
<div class="stats-table">
1040+
<div class="stats-row">
1041+
<span class="stats-label">Page count</span>
1042+
<span class="stats-value">1</span>
1043+
</div>
1044+
<div class="stats-row">
1045+
<span class="stats-label">Word count</span>
1046+
<span class="stats-value">42</span>
1047+
</div>
1048+
<div class="stats-row">
1049+
<span class="stats-label">Character count (with spaces)</span>
1050+
<span class="stats-value">256</span>
1051+
</div>
1052+
<div class="stats-row">
1053+
<span class="stats-label">Character count (without spaces)</span>
1054+
<span class="stats-value">230</span>
1055+
</div>
1056+
<div class="stats-row">
1057+
<span class="stats-label">Paragraph count</span>
1058+
<span class="stats-value">5</span>
1059+
</div>
1060+
<div class="stats-row">
1061+
<span class="stats-label">Non-Chinese word</span>
1062+
<span class="stats-value">30</span>
1063+
</div>
1064+
<div class="stats-row">
1065+
<span class="stats-label">Chinese character</span>
1066+
<span class="stats-value">120</span>
1067+
</div>
1068+
</div>
1069+
<div class="btn-row" style="margin-top: 20px;">
1070+
<button class="btn primary" data-log="Statistics: closeBridge.choose(0) 关闭">关闭</button>
1071+
</div>
1072+
</article>
1073+
<p class="legend">
1074+
对应 QML: <code>statsModel: [{label, value}, ...]</code>
1075+
<code>statsTitle: dialogTitle</code>
1076+
<code>buttonLabels: dialogButtons</code>
1077+
<code>onClicked -> closeBridge.choose(0)</code>
1078+
</p>
1079+
</section>
10021080
</main>
10031081
</div>
10041082

@@ -1009,21 +1087,24 @@ <h4>字号</h4>
10091087
confirm: document.getElementById('panel-confirm'),
10101088
form: document.getElementById('panel-form'),
10111089
font: document.getElementById('panel-font'),
1012-
paragraph: document.getElementById('panel-paragraph')
1090+
paragraph: document.getElementById('panel-paragraph'),
1091+
statistics: document.getElementById('panel-statistics')
10131092
};
10141093

10151094
const titleMap = {
10161095
confirm: 'ConfirmClose.qml 模拟',
10171096
form: 'FormDialog.qml 模拟',
10181097
font: 'FontSelector.qml 模拟',
1019-
paragraph: 'ParagraphFormat.qml 模拟'
1098+
paragraph: 'ParagraphFormat.qml 模拟',
1099+
statistics: 'Statistics.qml 模拟'
10201100
};
10211101

10221102
const codeMap = {
10231103
confirm: 'dialogMessage + dialogButtons + closeBridge.choose()',
10241104
form: 'formFields + values 收集 + closeBridge.submit(values)',
10251105
font: 'fontBridge 联动: family/style/filter/customize/preview',
1026-
paragraph: 'EnumCombo x N + tabs(基础/高级) + make-multi-line-with 写回'
1106+
paragraph: 'EnumCombo x N + tabs(基础/高级) + make-multi-line-with 写回',
1107+
statistics: 'statsModel: [{label, value}, ...] + closeBridge.choose(0) 关闭'
10271108
};
10281109

10291110
const viewTitle = document.getElementById('view-title');

devel/2039.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# [2039] 将 tools->Statistics 更换为 QML 实现
2+
3+
## 1 相关文档
4+
- [dddd.md](dddd.md) - 任务文档模板
5+
- [ai-docs/qml/README.md](../ai-docs/qml/README.md) - QML 对话框开发规范
6+
7+
## 2 任务相关的代码文件
8+
- `TeXmacs/progs/texmacs/texmacs/tm-tools.scm` — 统计数据计算与 `show-counts` 入口
9+
- `TeXmacs/tests/202_53.scm` — 统计数据 SCM 测试
10+
- `src/Plugins/Qt/qml/Statistics.qml` — 新建 QML 统计对话框
11+
- `src/Plugins/Qt/qml/atoms/Theme.qml` — 新增布局常量
12+
- `src/Plugins/Qt/QTMQmlDialog.cpp` / `.hpp` — 新增 `cpp_statistics_dialog` glue 入口
13+
- `src/Plugins/Qt/qml/qmldir` / `moganqml.qrc` — 注册 Statistics.qml
14+
- `src/Scheme/L5/glue_qt.lua` — glue 声明
15+
- `tests/Plugins/Qt/qml_load_test.cpp` — QML 加载测试
16+
- `ai-docs/qml/qml-dialog.html` — 设计稿新增 Statistics 面板
17+
- `ai-docs/qml/README.md` — 补充三条 QML 开发规则
18+
19+
## 3 如何测试
20+
21+
### 3.1 确定性测试(单元测试)
22+
```bash
23+
xmake b stem && xmake r 202_53 # SCM 数据契约测试(9 项 check)
24+
xmake b qml_load_test && xmake r qml_load_test # QML 加载测试(含 test_statistics_loads)
25+
```
26+
27+
### 3.2 非确定性测试(文档验证)
28+
```bash
29+
xmake b stem && xmake r stem
30+
# 打开 Tools -> Statistics,确认弹窗显示 7 项统计、Close 按钮关闭
31+
# 切换语言确认标签翻译生效(中文走 translate,字典自动拼接)
32+
```
33+
34+
## 4 如何提交
35+
36+
提交前执行以下最少步骤:
37+
38+
```bash
39+
gf fmt --changed-since=main
40+
xmake b stem && xmake r 202_53
41+
xmake b qml_load_test && xmake r qml_load_test
42+
```
43+
44+
## 5 What
45+
46+
将文档统计信息对话框从旧 `tm-widget` / `dialogue-window` 体系迁移到项目 QML 弹窗体系。
47+
48+
1. 新建 `Statistics.qml`(DialogShell + Column Repeater + DialogButtons),纯展示、一次性提交
49+
2. 新增 C++ glue 入口 `cpp_statistics_dialog`,走 `run_qml_dialog`(exec 阻塞模态)
50+
3. 抽取 `get-statistics-data` / `get-statistics-data-from-doc` 数据层,`show-counts` 改用 QML
51+
4. 扩展 SCM 测试覆盖数据组装层,新增 QML 加载测试
52+
5. 设计稿 `qml-dialog.html` 补 Statistics 面板,README 补三条开发规则
53+
54+
## 6 Why
55+
56+
旧实现用 TeXmacs 遗留的 `tm-widget``show-counts-widget`),与项目已有 QML 弹窗体系(ConfirmClose / FormDialog / FontSelector / ParagraphFormat)不统一。迁移后:
57+
- UI 风格统一(DialogShell + DialogButtons + Theme)
58+
- 统计项 label/value 分离,支持差异化样式
59+
- 移除 `tm-widget` 依赖,简化代码
60+
61+
## 7 How
62+
63+
### 7.1 数据层(scm)
64+
- `compute-stats-data doc page-str`:共享实现,返回 `((label value) ...)`
65+
- `get-statistics-data``show-counts` 调用,从 editor 取文档树和页数
66+
- `get-statistics-data-from-doc doc`:测试调用,无 editor,页数写死 `"0"`
67+
- `statistics-data->stree data`:转 `(stats (item <label> <value>) ...)`,label/value 作 compound 子节点(原子 string),避免 `get_label(compound)` 对中文加引号
68+
69+
### 7.2 C++ glue
70+
- `cpp_statistics_dialog(title, items)`:遍历 tree children 解析 `(item label value)`,组装 `QVariantList` 注入 QML context property `statsItems`
71+
-`run_qml_dialog`(exec 阻塞模态),无需 live 写回
72+
- 用户可见文本(label、标题)在 scm 侧用 `(translate ...)` 翻译,cpp/QML 纯透传
73+
74+
### 7.3 QML 界面
75+
- `Statistics.qml``DialogShell` 根,Column 内含标题 + Repeater(统计行)+ DialogButtons(Close)
76+
- context property:`statsTitle` / `statsItems` / `dialogButtons` / `dpScale` / `isDark` / `closeBridge`
77+
- 布局尺寸全引用 Theme 常量(`Theme.margin` / `titleH` / `textRowH` / `btnH` / `padS` / `pad` / `gapS` / `inlineGap` / `fontBody`),只保留本弹窗专属的 `labelW`(列宽比 0.62)
78+
- 按钮区高度 = `Theme.btnH + Theme.padS * 2`(按钮高 + 上下小留白)
79+
80+
### 7.4 新增 Theme 常量
81+
- `textRowH: 24` — 纯文本行高(区别于 `rowH: 44` EnumCombo 行)
82+
- `titleH: 28` — 弹窗标题区高度
83+
- `inlineGap: 12` — 行内 label↔value 间隙

0 commit comments

Comments
 (0)