r3 复审:那段「仍然共享」的成本数字与代码事实不符(三条填页线据此取 scope,故按 p1 处理)。 本轮**只改文档/注释**,无产品代码与测试逻辑改动(唯一非注释改动是 t2 断言的 message 字符串)。 - 第 4 条「读页外真值」:`States` 没有 `Default`,加字段会让 4 处 struct literal 全部 E0063 (`pages/mod.rs:154/177/232` + `app/view.rs:70`)⇒ 把 model 塞进 `States` 是 **3 个共享文件** (含 M104 的落点 `app/view.rs`);改推「给 `settings::view` 加 `model` 形参」这个**真·2 个文件** 的形状。并订正「在 `app/state.rs` 用真值初始化(1 个文件)」——今天没有语言真值源 (`AppearancePrefs` 无 language 字段,唯一值是 `app/view.rs:128` 硬编码 `Lang::En`)。 - 第 5 条第 ② 条路(改 `Body` 返回 `Task`)**删掉**:`settings::view` → `pages::render` → `App::view` 一路返回 `Element`(iced 0.14 的 view 亦然,且 `view(&self)` 改不了状态),view 期间产出的 `Task` 没有出口;真做要 `settings/mod.rs` + `pages/mod.rs` + `app/view.rs` 三处且另造旁路,明确写「不建议」。 只留第 ① 条(`app/update.rs` 的 `Message::Boot` → `on_boot`,1 个文件)。 - 第 6 条与 `state.rs::demo_groups` 文档:「harness 侧改两处」→ **三处** (`pages/mod.rs` 的 `page` / `page_with_appearance` / `app_with_appearance`,行 156/179/234)。 - p3:`message.rs` 里守门路径改成 `app/update/settings/tests.rs`(并点名 `settings.rs` 只是 mod 声明点); `appearance_section.rs` 的「绞线」错字改「绊线」;断言 message 的 `Some(appearance::body)` 改成源码 实际的 `Some(body)`(本地 fn);`app/update/settings/tests.rs` 的「抓不住 #2」改成两种洞分开说 (`|` 续行放行;花括号形状的多行 pattern 是**静默跳过**,不是误红)。
86 lines
5.0 KiB
Rust
86 lines
5.0 KiB
Rust
//! 设置页的**状态漏斗**(M101 r2):与 [`super::message`] 的消息漏斗同构——一个组的状态归它自己的
|
||
//! 文件,[`Groups`] 只是一个按组排开的架子。
|
||
//!
|
||
//! ## 为什么要这一层(r1 复审的 P1)
|
||
//!
|
||
//! 消息漏斗解决了「加一支消息要改哪些文件」,但一组要画**真值**就得有数据进渲染器:r1 之前
|
||
//! `settings::view` 只收 `Option<&appearance::State>`,于是 java / download / language+appearance
|
||
//! 三条线要显示「当前运行时 / 当前并发数 / 当前语言」时,**都得改 `settings/mod.rs`**;带运行期状态
|
||
//! 的还要改 `pages/mod.rs` / `app/view.rs` / `app/state.rs`。四条线一铺开就是第二处四路撞车。
|
||
//!
|
||
//! 现在的形状:
|
||
//!
|
||
//! ```text
|
||
//! App.settings: pages::settings::Groups ← 唯一的所有者(app/state.rs 一行)
|
||
//! ├─ java: java::State → pages/settings/java.rs
|
||
//! ├─ download: download::State → pages/settings/download.rs
|
||
//! ├─ language_appearance: language_appearance::State → pages/settings/language_appearance.rs
|
||
//! └─ ai_about: ai_about::State → pages/settings/ai_about.rs
|
||
//!
|
||
//! settings::view(ui, metrics, States { appearance, groups }) ← 只读视图,按值传
|
||
//! └─ 每组的 GROUP.body(ui, states) 自己从 states 里取自己要的那份
|
||
//! ```
|
||
//!
|
||
//! **加/改某一组的字段** = 只改 `pages/settings/<组>.rs`(状态类型在它自己文件里)+ 用它自己的
|
||
//! handler 更新(`app/update/settings/<线>.rs`)。本文件、`pages/mod.rs`、`app/state.rs`、
|
||
//! `app/view.rs` **都不用动**——本 mission 的这半边判据就是这么验的(见报告「变异证据」)。
|
||
//!
|
||
//! **加一整条新线**(多一个组)才需要:本文件加一个字段、`message.rs` 加一个变体、
|
||
//! `app/update/settings.rs` 加一条臂。那是「加一条线」,不是「加一个字段」。
|
||
//!
|
||
//! ## 外观那条真控件路径(M93)没有被挤掉
|
||
//!
|
||
//! `appearance::State` 仍是 `App` 的字段(不复用、不搬走),[`States::appearance`] 原样借用它;
|
||
//! 「没有外观状态(harness)⇒ 画演示行」这条判据现在由 `appearance` 组自己的 `GROUP.body`
|
||
//! 表达(返回 `None` ⇒ [`super::frame::demo_card`]),与 r1 之前逐像素等价。
|
||
|
||
use std::sync::LazyLock;
|
||
|
||
use crate::pages::appearance;
|
||
|
||
use super::{ai_about, download, java, language_appearance};
|
||
|
||
/// 各组自己拥有的运行期状态:**一个组一个字段**,字段类型定义在那条线自己的文件里。
|
||
///
|
||
/// 字段的**内部**结构归各组(加字段只改它自己的文件);架子(有哪几个组)归这里。
|
||
#[derive(Debug, Default)]
|
||
pub struct Groups {
|
||
/// Java 分组的状态(`pages/settings/java.rs`)。
|
||
pub java: java::State,
|
||
/// Downloads 分组的状态(`pages/settings/download.rs`)。
|
||
pub download: download::State,
|
||
/// 语言 + 外观两组的状态(`pages/settings/language_appearance.rs`)。
|
||
pub language_appearance: language_appearance::State,
|
||
/// AI / About 分组的状态(`pages/settings/ai_about.rs`)。
|
||
pub ai_about: ai_about::State,
|
||
}
|
||
|
||
/// 画一帧时**按值**递给各组渲染器的只读视图:组状态 + 外观运行期状态(两个引用,`Copy`)。
|
||
///
|
||
/// 为什么按值而不是按引用:`Settings::view` 返回的 `Element<'a>` 会借走它,按引用传会逼着
|
||
/// harness 那条路径([`super::demo_groups`] 撑着的 `page()` / `page_with_appearance()`)为一份
|
||
/// **临时构造**的 `States` 造 `'static`,或者把默认状态塞进 `Model`(两份真相)。按值传没有
|
||
/// 这层借用纠缠,调用方写一行就够。
|
||
#[derive(Debug, Clone, Copy)]
|
||
pub struct States<'a> {
|
||
/// 外观运行期状态(M93):`None` = harness / 演示路径(设置页画演示行)。
|
||
pub appearance: Option<&'a appearance::State>,
|
||
/// 各组自己的状态(见 [`Groups`])。
|
||
pub groups: &'a Groups,
|
||
}
|
||
|
||
/// 演示 / harness 路径的默认组状态(`pages::page` 与 `pages::page_with_appearance` 用它)。
|
||
///
|
||
/// `'static` 是有意的:那两条入口不持有任何组状态,而返回的 `Element<'a>` 会借走这份 `Groups`;
|
||
/// 让它活得比调用方长,借用就不会把返回值钉在函数栈上。
|
||
///
|
||
/// **隐性代价(r2 复审 P3)**:`static` 要求 `Groups: Sync + Default`,于是某个组的 `State` 里一旦
|
||
/// 放了 `Rc` 这类非 `Sync` 字段,编译会红在**本文件**(共享)而不是那一组自己的文件。真需要
|
||
/// 非 `Sync` 状态时的改法:去掉这个 `static`,改成每个调用点自己造一份 `Groups`——`pages/mod.rs` 的
|
||
/// **三个**调用点都要改(`page` / `page_with_appearance` / `app_with_appearance`,行 156 / 179 / 234),
|
||
/// 一次性代价,见 [`super`] 文档「仍然共享」第 6 条。
|
||
pub fn demo_groups() -> &'static Groups {
|
||
static DEMO: LazyLock<Groups> = LazyLock::new(Groups::default);
|
||
&DEMO
|
||
}
|