Files
Unlucky-Launcher/crates/app-iced/src/app/adopt/shot.rs
T
AskaEth a5d1dec495
CI / fmt (rustfmt --check) (push) Waiting to run
CI / clippy (ubuntu-latest) (push) Waiting to run
CI / clippy (windows-latest) (push) Waiting to run
CI / test (ubuntu-latest) (push) Waiting to run
CI / test (windows-latest) (push) Waiting to run
CI / build release (ubuntu-latest) (push) Waiting to run
CI / build release (windows-latest) (push) Waiting to run
CI / runtime smoke (ubuntu x64, xvfb) (push) Waiting to run
CI / runtime smoke (macos arm64, probe) (push) Skipped
CI / update-e2e (windows x64) (push) Waiting to run
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Waiting to run
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Waiting to run
Guards / core 零 UI 守门 (push) Waiting to run
Guards / presenter 零 UI 守门 (push) Waiting to run
Guards / design 零第三方依赖守门 (push) Waiting to run
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Waiting to run
Guards / 文件行数 ≤400 守门 (push) Waiting to run
Guards / 依赖许可与来源(cargo-deny) (push) Waiting to run
Release guard / 发布标记语义守门(push tag → 稳定版) (push) Waiting to run
Signing status(播报,不是门禁) / 判据自测(三行/退出码/变异重放/接线) (push) Waiting to run
Signing status(播报,不是门禁) / 签名就绪度(播报) (push) Waiting to run
Release / 选槽位(默认四个:Windows 两档 + Linux 两档) (push) Waiting to run
release: v0.2.4 里程碑快照(孤儿提交,无父;树 = dev 68362e5)
本批内容见 CHANGELOG.md 的 0.2.4 段。按 D41 用孤儿快照推 main,保留「公开史只有里程碑」的模型。
2026-11-11 21:41:38 +08:00

206 lines
8.4 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.
//! 出图用的向导状态夹具(D30 视觉验收的输入)。
//!
//! ## 为什么要有它
//!
//! 向导是**四段有状态**的界面,而截图通道只跑一次 `view()`(第 3 帧落盘),没有真人点按钮。
//! 组件库的做法是 `--screen=gallery` + `ForcedState`,页面层的做法是 `pages/instances/demo.rs`
//! 的 `DEMO`——同一类要求,同一类做法:**给渲染器一份确定的状态**,而不是让截图去赌异步时序。
//!
//! ## 哪些是真、哪些是夹具(复审请照这条核对)
//!
//! | 内容 | 来源 |
//! |---|---|
//! | 候选 / 问题 / 失败原因 | **夹具**(合成的 id 与问题文本,只为把版面撑到真实形态) |
//! | 运行目录 / 隔离档 | 夹具(固定路径,便于两张图逐像素对照) |
//! | **后果原文** | **真**:`unlucky_core::game::adopt::target_notes()` 现算,一个字不抄 |
//! | 文案(标题/标签/按钮) | **真**:`unlucky_i18n` 目录 |
//! | 渲染 | **真**:`--software-renderer`(tiny-skia)1180×760,与产品路径同一棵控件树 |
//!
//! 也就是说:这些图证明**版面与文案**,不证明 core 的判据(判据的证据在 `host/adopt/tests.rs`
//! 与 `core/tests/adopt/`)。
//!
//! ## 怎么用
//!
//! ```text
//! ULL_ADOPT_STATE=pick|pick-selected|empty|preview|shared|failed|done \
//! unlucky-launcher-iced.exe --software-renderer --screenshot <path> [--screenshot-theme dark]
//! ```
//!
//! 只在 `--screenshot` 已经给了的情况下生效(交互模式读这个环境变量没有意义,直接忽略)。
use std::path::PathBuf;
use unlucky_core::game::adopt::{target_notes, AdoptTarget};
use unlucky_presenter::dto::{
AdoptCandidateView, AdoptDoneView, AdoptEvent, AdoptFailureView, AdoptIsolationView,
AdoptPlanView, AdoptProblemView, AdoptScanView, AdoptState, AdoptTargetView,
};
use super::{Busy, Form};
/// 夹具里的版本目录(只是**画**出来的路径,不碰盘)。
const VERSIONS_DIR: &str = "D:/minecraft/.minecraft/versions";
/// 夹具里的数据区运行目录。
const INSTANCE_RUN_DIR: &str = "D:/ULL/.ULL/data/instances/Better-MC";
/// 夹具里的游戏根。
const GAME_ROOT: &str = "D:/minecraft/.minecraft";
/// 环境变量名(见模块文档)。
pub(crate) const ENV: &str = "ULL_ADOPT_STATE";
/// 按名字造一份「打开着的向导」;不认识的名字返回 `None`(不影响正常启动)。
pub(crate) fn seed(name: &str) -> Option<(Form, AdoptState)> {
let name = name.trim().to_ascii_lowercase();
let mut form = Form {
open: true,
..Form::default()
};
let mut state = AdoptState::default();
match name.as_str() {
// 扫描完成、还没选版本(列表里三态齐全:可导入 / 已登记 / 有问题)
"pick" => {
state.apply(AdoptEvent::Scan(Box::new(scan_view())));
form.busy = Busy::Idle;
}
// 扫描完成、**已选中一个可导入的版本**(选中行底色 + 预检按钮启用态)
"pick-selected" => {
state.apply(AdoptEvent::Scan(Box::new(scan_view())));
form.selected = Some("1.20.1".to_string());
form.busy = Busy::Idle;
}
// 游戏根下真没有版本(空态)
"empty" => {
state.apply(AdoptEvent::Scan(Box::new(AdoptScanView {
versions_dir: PathBuf::from(VERSIONS_DIR),
candidates: Vec::new(),
})));
form.busy = Busy::Idle;
}
// 选中缺省档(InstanceDir)并点过预检
"preview" => {
state.apply(AdoptEvent::Scan(Box::new(scan_view())));
state.apply(AdoptEvent::Planned(Box::new(plan_view(
AdoptTargetView::InstanceDir,
AdoptIsolationView::Custom,
INSTANCE_RUN_DIR,
))));
form.selected = Some("1.20.1".to_string());
form.checked = true;
form.busy = Busy::Idle;
}
// 选中共享档(Shared):后果原文由 core `target_notes()` 现算
"shared" => {
state.apply(AdoptEvent::Scan(Box::new(scan_view())));
state.apply(AdoptEvent::Planned(Box::new(plan_view(
AdoptTargetView::Shared,
AdoptIsolationView::Shared,
GAME_ROOT,
))));
form.selected = Some("1.20.1".to_string());
form.target = AdoptTargetView::Shared;
form.checked = true;
form.busy = Busy::Idle;
}
// 预检失败:两条问题逐条列出(不许折叠成一句)
"failed" => {
state.apply(AdoptEvent::Scan(Box::new(scan_view())));
state.apply(AdoptEvent::Failed(Box::new(AdoptFailureView {
code: "adopt_not_adoptable".to_string(),
message: "版本 1.19.4 不能登记:清单不是合法 JSON".to_string(),
problems: vec![
AdoptProblemView {
code: "manifest_invalid_json".to_string(),
message: format!(
"清单不是合法 JSON {VERSIONS_DIR}/1.19.4/1.19.4.json:expected `,` or `}}` at line 12 column 3"
),
},
AdoptProblemView {
code: "missing_parent".to_string(),
message: "版本 1.19.4 继承的父版本 1.19.3 不在盘上".to_string(),
},
],
})));
form.selected = Some("1.19.4".to_string());
form.checked = true;
form.busy = Busy::Idle;
}
// 登记完成
"done" => {
state.apply(AdoptEvent::Scan(Box::new(scan_view())));
state.apply(AdoptEvent::Done(Box::new(AdoptDoneView {
instance_id: "2f1c0a5e-7f4b-4c1d-9a2e-6b8d5c3a1f70".to_string(),
name: "Better MC".to_string(),
version_id: "1.20.1-fabric-0.15.0".to_string(),
isolation: AdoptIsolationView::Custom,
run_dir: PathBuf::from(INSTANCE_RUN_DIR),
dir_created: true,
notes: target_notes(&AdoptTarget::InstanceDir, &PathBuf::from(INSTANCE_RUN_DIR)),
})));
form.selected = Some("1.20.1-fabric-0.15.0".to_string());
form.checked = true;
form.busy = Busy::Idle;
}
_ => return None,
}
Some((form, state))
}
/// 一份扫描结果(三态齐全)。
fn scan_view() -> AdoptScanView {
AdoptScanView {
versions_dir: PathBuf::from(VERSIONS_DIR),
candidates: vec![
AdoptCandidateView {
version_id: "1.20.1".to_string(),
jar_present: true,
inherits: Vec::new(),
registered: Vec::new(),
problems: Vec::new(),
},
AdoptCandidateView {
version_id: "1.20.1-fabric-0.15.0".to_string(),
jar_present: false,
inherits: vec!["1.20.1-fabric-0.15.0".to_string(), "1.20.1".to_string()],
registered: vec!["Better MC".to_string()],
problems: Vec::new(),
},
AdoptCandidateView {
version_id: "1.19.4".to_string(),
jar_present: true,
inherits: Vec::new(),
registered: Vec::new(),
problems: vec![AdoptProblemView {
code: "manifest_invalid_json".to_string(),
message: format!(
"清单不是合法 JSON {VERSIONS_DIR}/1.19.4/1.19.4.json:expected `,` or `}}` at line 12 column 3"
),
}],
},
],
}
}
/// 一份预检结果。**`notes` 由 core 现算**(共享档/版本目录档的后果原文只有一处)。
fn plan_view(
target: AdoptTargetView,
isolation: AdoptIsolationView,
run_dir: &str,
) -> AdoptPlanView {
let core_target = match target {
AdoptTargetView::InstanceDir => AdoptTarget::InstanceDir,
AdoptTargetView::Shared => AdoptTarget::Shared,
AdoptTargetView::VersionDir => AdoptTarget::VersionDir,
};
AdoptPlanView {
version_id: "1.20.1".to_string(),
name: "1.20.1".to_string(),
target,
isolation,
run_dir: PathBuf::from(run_dir),
notes: target_notes(&core_target, &PathBuf::from(run_dir)),
}
}