Files
Unlucky-Launcher/crates/app-iced/src/pages/settings/state.rs
T
AskaEth d3e959e754 docs(app-iced): M101 r4 —— 订正共享点成本数字(3 / 不可行 / 3 处)与三处 p3 措辞
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 是**静默跳过**,不是误红)。
2026-11-03 23:59:07 +08:00

86 lines
5.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 设置页的**状态漏斗**(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
}