fix(app-iced): 必修1 「72 字符」改成逐行口径,上限 435 → 424
代码 r2 的复核:`435 / 6.0526 = 71.9` 是**整段平均步进**换来的数,不是每行都 ≤72; 它实测交付图里就有 **73 字符**行(`guess. Nothing on this page is wired to real data yet: the layout is, the`, ink 428px)。上一轮是系数错,这一轮是**把平均当成了逐行上界**。 选 **(a)**:不变式改成逐行可验证,cap 跟着压到能满足它的地方。依据是 FreeType 量宽 + 贪心词折行的模拟表 (同一段 361 字符正文扫列宽): | 列宽 | 最长一行 | 该行 ink | 判定 | |---|---|---|---| | 435(= 平均口径上界) | 73 字符 | 428px | ✗ | | 428 | 73 字符 | 428px | ✗ | | 426 | 71 字符 | 426px | ✓ 只剩 1 字符余量 | | **424**(4px 栅格) | **67 字符** | **402px** | ✓ 取这个 | - `measure_cap_px()` = `min(逐行安全宽 424, 平均口径上界 435)` = **424**;平均口径另立 `measure_average_bound_px()`(435)并在文档里标明「只是上界,不是取值」。 - 单测 `line_length_is_capped_at_72_characters` 改成**两侧夹逼**:`402 ≤ cap < 428` —— 放大到 428 会重新出现 73 字符行(红),压到 402 以下白丢宽度(也红);另断言 `cap < measure_average_bound_px()`,防止有人把平均口径当上限。 - §3 新增「逐行口径」小节:模拟表 + **作用域**(承诺只覆盖 14px 正文;12px 辅助文案是 1~2 行短句, 不做逐行承诺;CJK 全角 1em 另行定)。 - 四页文档与 `pages/mod.rs` 的 ≤435 同步改 424。
This commit is contained in:
1 parent
a650f8184b
commit
ddf6937b2b
7 files changed
+93
-29
No files matched your search
@@ -51,10 +51,28 @@
|
||||
//! > 现在事故描述一律用**实测步进 × 实际字号**;这也是
|
||||
//! > `line_length_is_capped_at_72_characters` 里那条「旧口径 504px 能放 83 字符」护栏的由来。
|
||||
//!
|
||||
//! 所以正文列宽有**硬上限** [`measure_cap_px`]:按**实测**的 `BodyMedium` 14px 步进
|
||||
//! 6.05px/字符(= 0.4323em)算出 **72 字符 = 435px**,由 [`content_column_width`] 与
|
||||
//! [`frame_metrics`] 无条件施加:任何窗口宽度、任何版面,正文列都不会超过它。
|
||||
//! 1180 宽窗口下正文列是 **435**(不是 712)。
|
||||
//! ### 逐行口径:72 字符是**最长一行**的上界,不是平均(r2 复审的教训)
|
||||
//!
|
||||
//! 「平均步进 × 72」只能保证**平均**一行 72 字符:r2 复审在交付图里抓到一行 **73 字符**
|
||||
//! (`guess. Nothing on this page is wired to real data yet: the layout is, the`,ink 428px)。
|
||||
//! 所以上限**不是算术结果,而是逐行折行模拟的结果**——用 FreeType 量宽 + 贪心词折行,
|
||||
//! 对实例页那段正文(361 字符)扫不同列宽:
|
||||
//!
|
||||
//! | 列宽 | 最长一行 | 该行 ink | 结论 |
|
||||
//! |---|---|---|---|
|
||||
//! | 435(= 平均口径上界) | 73 字符 | 428px | ✗ 破了 72 |
|
||||
//! | 428 | 73 字符 | 428px | ✗ |
|
||||
//! | 426 | 71 字符 | 426px | ✓ 但只剩 1 字符余量 |
|
||||
//! | **424**(4px 栅格) | **67 字符** | **402px** | ✓ 取这个:满足上界 + 留 5 字符余量 |
|
||||
//!
|
||||
//! 于是 [`measure_cap_px`] = **424**(= `min(逐行安全宽 424, 平均口径上界 435)`),由
|
||||
//! [`content_column_width`] 与 [`frame_metrics`] 无条件施加:任何窗口宽度、任何版面,
|
||||
//! 正文列都不会超过它。1180 宽窗口下正文列是 **424**(不是 712)。
|
||||
//!
|
||||
//! **作用域**:这条承诺只覆盖 `BodyMedium` 14px 的正文。`BodySmall` 12px 的辅助文案
|
||||
//! (空态副文案、窄窗提示)在同一列宽下每行可到 ~82 字符——它们都是 1~2 行的短句,不做逐行承诺;
|
||||
//! 中文(全角 1em)另算,等 i18n mission 定。
|
||||
|
||||
//!
|
||||
//! ### 步进是怎么量的(口径 + 可复现)
|
||||
//!
|
||||
@@ -63,7 +81,7 @@
|
||||
//! - **样本**:实例页详情那段正文(`pages/instances/data.rs` 的 `NOTE`,**361 字符**,
|
||||
//! 大小写 / 标点 / 空格 / `→` 混排,是我们自己的真实正文,不是造出来的 `mmmm`);
|
||||
//! - **量法**:FreeType `getlength`(PIL `ImageFont.truetype(segoeui.ttf, 14)`)求整段排版宽,
|
||||
//! 再除以字符数 → **2185.00px / 361 = 6.0526 px/字符 = 0.43233em**;× 72 = 435.79 → **floor 435**;
|
||||
//! 再除以字符数 → **2185.00px / 361 = 6.0526 px/字符 = 0.43233em**;× 72 = 435.79(这是**平均**口径上界,逐行口径见下);
|
||||
//! - **交叉验证**:同一段的首行(78 字符)渲染实测墨迹 **484px**,FreeType 对同一串给 479px
|
||||
//! (差 ~1%,抗锯齿外扩),说明「步进」这个口径在 iced 渲染器与 FreeType 之间是一致的;
|
||||
//! - **CJK 不适用**:全角字形是 1em(14px),中文的 72 字符是 1008px。页面文案走 i18n 之后
|
||||
@@ -133,12 +151,24 @@ pub const fn rail_width() -> f32 {
|
||||
LayoutMetrics::DESKTOP.nav_rail_width
|
||||
}
|
||||
|
||||
/// 正文列宽上限(px):`floor(14px × 0.43233em × 72 字符)` = **435**。
|
||||
/// **逐行**安全宽度(px):折行模拟下「最长一行 ≤72 字符」的最大 4px 栅格列宽 = **424**。
|
||||
///
|
||||
/// 见模块文档 §3 的模拟表:428 会出现 73 字符行,426 起降到 71,424 给到 67(留 5 字符余量)。
|
||||
/// 这个数是**量出来的**,不是 `advance × 72` 算出来的——后者只保证平均。
|
||||
pub const LINE_SAFE_WIDTH_PX: f32 = 424.0;
|
||||
|
||||
/// 平均口径上界(px):`floor(14px × 0.43233em × 72)` = **435**。
|
||||
///
|
||||
/// 只能保证**平均**一行 72 字符(实测 435 下会冒出 73 字符行),所以它是上界而不是取值。
|
||||
pub fn measure_average_bound_px() -> f32 {
|
||||
(TypeRole::BodyMedium.style().size_px * CHAR_ADVANCE_EM * MAX_MEASURE_CHARS).floor()
|
||||
}
|
||||
|
||||
/// 正文列宽上限(px)= `min(逐行安全宽 424, 平均口径上界 435)` = **424**。
|
||||
///
|
||||
/// **向下取整**:算出来是 435.79,四舍五入成 436 就会让最后一两个字符挤不进「72 字符」的承诺。
|
||||
/// 硬上限,任何版面 / 任何窗口宽度都不许超;`1180` 窗宽下正文列就是它(而非 M68 的 712)。
|
||||
pub fn measure_cap_px() -> f32 {
|
||||
(TypeRole::BodyMedium.style().size_px * CHAR_ADVANCE_EM * MAX_MEASURE_CHARS).floor()
|
||||
LINE_SAFE_WIDTH_PX.min(measure_average_bound_px())
|
||||
}
|
||||
|
||||
/// 正文列宽下限(px):`floor(14 × 0.43233 × 45)` = **272**。
|
||||
@@ -272,7 +302,7 @@ pub fn available_width(window_width: f32) -> f32 {
|
||||
/// - 单列页:就是正文列宽(水平居中摆放);
|
||||
/// - 两列页:这是**主列**的正文列宽;实际值还要看 [`frame_metrics`](详情列会被列表列挤窄)。
|
||||
///
|
||||
/// 返回值永远 ≤ [`measure_cap_px`]:1180 窗宽下是 435(实测 72 字符),不是 M68 的 712。
|
||||
/// 返回值永远 ≤ [`measure_cap_px`]:1180 窗宽下是 424(逐行 67 字符,≤72),不是 M68 的 712。
|
||||
pub fn content_column_width(window_width: f32) -> f32 {
|
||||
available_width(window_width).min(measure_cap_px())
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user