feat(m175): Java 设置页接真运行时(扫描 / 手动路径 / 默认 JDK / 内存 / JVM 参数)

用户实测四条抱怨逐条对应:
- 「没有自动检测本机 Java」:首屏自动扫一次盘(托管根 + JAVA_HOME + PATH + 标准安装位置 +
  注册表),之后由「立即扫描」手动触发;扫描在后台线程里跑,带 done/total 进度与取消
- 「显示 Temurin 21.0.4 不知道是不是硬编码」:演示行与绊线测试一起删掉,列表来自真读盘
- 「没一个能改」:默认内存(MB,512~65536,范围与 schema 同源)与 JVM 参数可真改、真落盘、读回
- 「不能手动指定 Java 路径 / 不能改默认版本」:手动添加走严格校验(真跑 java -version),
  设为默认写 java.selected_path 并给出「为什么选它」(当前实例的版本门槛 + 每行满足与否)

core:java::manual(严格探测 + 厂商识别 + 名单持久化)、java::staged(候选收集 + 带进度与
取消的扫描)、java::proc(带超时且可打断的子进程执行:卡住的 java -version 会被 kill)。
i18n:catalog/settings/java.rs 55 条中英 + 金表按装配顺序插入。

复审 r1(p2-5items)全修:
- P2-1 「设为默认」不再清脏:只写 java.selected_path,内存/JVM 草稿一个字节都不动
- P2-2 错误文案全部走 i18n(三档「写不进去」/「路径太长」/「名单写不进去」各自一个 key),
  不再把 Rust 枚举名当人话
- P2-3 手动名单落盘有正面断言(加一条 → 从盘上新读一份 → 在)
- P2-4 版本号显示到小版本(17.0.13 / 17.0.9 可区分),解析不出来时给原始版本串
- P2-5 每候选 8 秒超时并 kill、取消打断正在跑的探测、扫描槽挂在 State 上、线程活着期间
  不许起第二个(界面显示「正在取消…」)

复审 r2(p2-2items)全修:
- P2-1 空态不再指向不存在的「Java 页」,改成真实存在的 `ull java install 21` 命令行入口
- P2-2 `auto_scan_due` 增加 `!report.cancelled`:取消过一次之后本进程不再自动扫,
  配置文件再变(存内存 / 改主题)也不会把取消无声撤销;手动「立即扫描」不受影响
- 顺带:proc 的 kill+wait 补「子进程真死了」的回归守卫(标记文件从未出现)、
  core 的 Schema 错误改成页面自己的 key(不再把中文 Display 直出给英文界面)、
  默认态 chip 用新的 `Chip::badge`(不再挂一条永远收不到的消息)
This commit is contained in:
AskaEth committed 2026-11-08 19:52:04 +08:00
1 parent 30b61e2684
commit df331f6a60
26 files changed
+4030 -85

No files matched your search

+59
View File
@@ -29,6 +29,65 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
("SettingsAppearanceCornerSquare", "Square", "直角"),
("SettingsAppearanceCornerHint", "On Windows 11 and Server 2025 we ask the system for rounded corners; Windows 10 keeps square corners. On macOS and Linux the desktop's own rules apply. We never draw corners ourselves.", "Windows 11 与 Windows Server 2025 上我们请求系统处理外框圆角;Windows 10 保持直角;macOS 与 Linux 交给桌面合成器自己的规则;我们不自己画圆角。"),
("SettingsAppearanceCornerSuspended", "Not in effect right now: window transparency is below 100%. Setting it back to 100% restores the rounded corners.", "当前因整窗透明度不生效:把整窗透明度调回 100% 圆角即恢复。"),
// M175:设置页「Java」分组(`catalog/settings/java.rs` 的 `settings_java_entries`)。
// 位置就是装配链里那个域的位置:`settings_java_entries` 在 `settings_appearance_entries` 之后、
// `settings_download_entries`(空)与 `settings_ai_entries`(M174)之前,所以这几行必须落在
// `SettingsAppearanceCornerSuspended` 与 `SettingsGroupAssistant` 之间(顺序 = `Key::ALL`)。
("SettingsJavaScan", "Scan now", "立即扫描"),
("SettingsJavaScanningStart", "Scanning…", "正在扫描…"),
("SettingsJavaScanning", "Scanning… {done}/{total}", "正在扫描… {done}/{total}"),
("SettingsJavaCancelling", "Cancelling…", "正在取消…"),
("SettingsJavaCancel", "Cancel", "取消"),
("SettingsJavaCancelled", "The scan was cancelled; the list below is from the previous run.", "扫描已取消;下面的列表还是上一次的结果。"),
("SettingsJavaScanDone", "{count} JDK found", "找到 {count} 个 JDK"),
("SettingsJavaNeverScanned", "Not scanned yet", "还没扫描过"),
("SettingsJavaNeverScannedBody", "Press Scan to look for installed JDKs (registry, common install folders, JAVA_HOME and PATH). Nothing runs until you do.", "点「立即扫描」去找已装的 JDK(注册表、常见安装目录、JAVA_HOME 与 PATH)。在那之前启动器不会自己去扫。"),
("SettingsJavaEmptyTitle", "No Java runtime found", "未检测到 Java 运行时"),
("SettingsJavaEmptyBody", "Nothing matched. Add the path of a JDK you already unpacked below, or install one with `ull java install 21` on the command line.", "没找到任何 JDK。可以在下面手动加上你自己解压的那份,或者用命令行 `ull java install 21` 装一个。"),
("SettingsJavaDefault", "Default", "默认"),
("SettingsJavaSetDefault", "Use as default", "设为默认"),
("SettingsJavaFits", "Meets the current instance requirement", "满足当前实例的要求"),
("SettingsJavaTooOld", "Below the requirement: this instance needs Java {minimum}+", "低于要求:当前实例需要 Java {minimum} 及以上"),
("SettingsJavaDefaultSet", "Default JDK is now {path}", "默认 JDK 已设为 {path}"),
("SettingsJavaSourceManaged", "installed by this launcher", "本启动器安装"),
("SettingsJavaSourceEnv", "JAVA_HOME (environment variable)", "JAVA_HOME(环境变量)"),
("SettingsJavaSourcePath", "on PATH", "PATH 上"),
("SettingsJavaSourceSystem", "system install", "系统安装"),
("SettingsJavaSourceManual", "added by you", "手动添加"),
("SettingsJavaAdd", "Add a JDK path", "手动添加 JDK 路径"),
("SettingsJavaAddPlaceholder", "C:\\Program Files\\Java\\jdk-21", "例:C:\\Program Files\\Java\\jdk-21"),
("SettingsJavaAddHint", "A JDK folder or its java executable. We run `java -version` on it; anything that cannot answer is refused.", "JDK 目录或它的 java 可执行文件。会真跑一次 `java -version`;答不出话的一律拒绝。"),
("SettingsJavaAddAction", "Add", "添加"),
("SettingsJavaAdded", "Added {path}", "已添加 {path}"),
("SettingsJavaMemory", "Default memory (MB)", "默认内存(MB)"),
("SettingsJavaMemoryHint", "Whole megabytes, {min}–{max}. Instances without their own override follow this value.", "整数 MB,范围 {min}~{max}。没有实例级覆盖的实例都用它。"),
("SettingsJavaLayerGlobal", "In effect: {mb} MB, from the global default.", "生效:{mb} MB,来自全局默认值。"),
("SettingsJavaLayerInstance", "In effect: {mb} MB, set by instance {instance}. Changing the global value below will not affect that instance.", "生效:{mb} MB,由实例 {instance} 覆盖。改下面的全局值不会影响它。"),
("SettingsJavaJvm", "Extra JVM arguments", "额外 JVM 参数"),
("SettingsJavaJvmPlaceholder", "-XX:+UseG1GC -Dfile.encoding=UTF-8", "例:-XX:+UseG1GC -Dfile.encoding=UTF-8"),
("SettingsJavaJvmHint", "Separated by spaces, quotes allowed. Appended to the command line of every launch; an instance's own JVM arguments come after it.", "按空白分隔,支持引号。会拼到每次启动的命令行上;实例自己的 JVM 参数排在它后面。"),
("SettingsJavaJvmSaved", "Saved value: {value}", "已保存的值:{value}"),
("SettingsJavaJvmSavedNone", "No extra JVM arguments are saved yet.", "还没有保存任何额外参数。"),
("SettingsJavaSave", "Save", "保存"),
("SettingsJavaSaved", "Saved and read back from disk. The next launch uses the new value.", "已保存,并从磁盘读回确认。下次启动就用新值。"),
("SettingsJavaUnsaved", "There are unsaved changes.", "有未保存的改动。"),
("SettingsJavaRefused", "Not saved: {reason}", "未保存:{reason}"),
("SettingsJavaSkipNotDirty", "there was nothing to change, so no file was written", "没有任何改动,因此没有写文件"),
("SettingsJavaSkipReadOnly", "the settings file is read-only: a newer launcher wrote it, or it is marked untrusted", "配置文件只读:它被更新版本的启动器写过,或被标记为不可信"),
("SettingsJavaSkipUnreadable", "the settings file is untrusted, so it is treated as read-only", "配置文件不可信,因此按只读处理"),
("SettingsJavaNoStorage", "No settings file is available in this run, so nothing can be scanned or saved.", "这一次运行没有可用的设置文件,扫描与保存都做不了。"),
("SettingsJavaReasonMemoryEmpty", "the memory field is empty", "内存没填"),
("SettingsJavaReasonMemoryNotNumber", "the memory must be a whole number of MB", "内存必须是整数 MB"),
("SettingsJavaReasonMemoryRange", "the memory is outside the allowed range", "内存超出允许范围"),
("SettingsJavaReasonJvmQuotes", "the JVM arguments have an unclosed quote", "JVM 参数里有没配对的引号"),
("SettingsJavaReasonAddEmpty", "the path is empty", "路径是空的"),
("SettingsJavaReasonAddMissing", "nothing exists at that path", "那个路径上什么都没有"),
("SettingsJavaReasonAddNotJava", "there is no java executable at that path", "那个路径里没有 java 可执行文件"),
("SettingsJavaReasonAddNoVersion", "java -version did not answer there, so it is not a usable JDK", "在那里跑不出 java -version,不是一份能用的 JDK"),
("SettingsJavaReasonAddTooLong", "the path is too long to be a JDK location", "路径太长,不像是一个 JDK 位置"),
("SettingsJavaReasonAddWriteFailed", "the JDK is fine, but the list file could not be written (disk or permission)", "这份 JDK 没问题,但名单文件写不进去(磁盘或权限)"),
("SettingsJavaReasonAddDuplicate", "that JDK is already in the list", "这份 JDK 已经在列表里了"),
("SettingsJavaReasonWriteFailed", "the settings file could not be written (disk full, no permission, or the value was rejected)", "设置写不进去(磁盘满、没权限,或这个值被拒绝)"),
// M174:设置页「助手(AI)」分组(`catalog/settings/ai.rs` 的 `settings_ai_entries`)。
// 位置就是装配链里那个域的位置:`settings_ai_entries` 在 `settings_download_entries` 之后、
// `core_text_entries` 之前,所以这几行必须落在 `SettingsAppearanceCornerSuspended` 与
+147 -4
View File
@@ -1,11 +1,154 @@
//! 设置页「Java」分组文案:**预留**(页面侧的真控件还没接)。
//! 设置页「Java」分组文案(M175):扫描 / 运行时列表 / 默认 JDK / 内存 / JVM 参数。
//!
//! 加 key 时把条目写进 `$($acc)*` 后面即可(形状见 [`crate::catalog`] 的「域装配」一节)。
//! 现在没有条目,所以本宏是**空转移器**:只把累积表原样交给下一个域。
//! 域文件的形状见 [`crate::catalog`] 的「域装配」一节。本域在装配链里排在
//! `settings_appearance_entries` 之后、`settings_download_entries` 之前,所以金表那批行要插在
//! `SettingsAppearanceCornerSuspended` 之后、`SettingsGroupAssistant`(M174 的助手域)之前
//! ——**顺序即 `Key::ALL`**。
//!
//! 三条口径:
//!
//! 1. 技术名词不翻:`JDK` / `JVM` / `-version` / `bin/java` / `java.selected_path` 原样;
//! 2. **不许出现写死的版本号或数字**:本组以前那三行(`Temurin 21.0.4` / `4 GiB`)正是被用户
//! 当成假数据的那三条,现在所有数值都由页面从真读盘的结果里格式化进来;
//! 3. 拒绝原因一句一条(空 / 不存在 / 不是 java / 跑不出 -version / 重复),合成一句
//! 「路径无效」等于让用户自己猜。
macro_rules! settings_java_entries {
($next:ident, $($rest:ident,)* { $($acc:tt)* }) => {
$next! { $($rest,)* { $($acc)* } }
$next! { $($rest,)* {
$($acc)*
// ── 扫描(状态 / 进度 / 取消)
SettingsJavaScan => ("Scan now", "立即扫描"),
SettingsJavaScanningStart => ("Scanning…", "正在扫描…"),
SettingsJavaScanning => ("Scanning… {done}/{total}", "正在扫描… {done}/{total}"),
SettingsJavaCancelling => ("Cancelling…", "正在取消…"),
SettingsJavaCancel => ("Cancel", "取消"),
SettingsJavaCancelled => (
"The scan was cancelled; the list below is from the previous run.",
"扫描已取消;下面的列表还是上一次的结果。"
),
SettingsJavaScanDone => ("{count} JDK found", "找到 {count} 个 JDK"),
SettingsJavaNeverScanned => ("Not scanned yet", "还没扫描过"),
SettingsJavaNeverScannedBody => (
"Press Scan to look for installed JDKs (registry, common install folders, JAVA_HOME and PATH). Nothing runs until you do.",
"点「立即扫描」去找已装的 JDK(注册表、常见安装目录、JAVA_HOME 与 PATH)。在那之前启动器不会自己去扫。"
),
SettingsJavaEmptyTitle => ("No Java runtime found", "未检测到 Java 运行时"),
SettingsJavaEmptyBody => (
"Nothing matched. Add the path of a JDK you already unpacked below, or install one with `ull java install 21` on the command line.",
"没找到任何 JDK。可以在下面手动加上你自己解压的那份,或者用命令行 `ull java install 21` 装一个。"
),
// ── 运行时列表
SettingsJavaDefault => ("Default", "默认"),
SettingsJavaSetDefault => ("Use as default", "设为默认"),
SettingsJavaFits => ("Meets the current instance requirement", "满足当前实例的要求"),
SettingsJavaTooOld => (
"Below the requirement: this instance needs Java {minimum}+",
"低于要求:当前实例需要 Java {minimum} 及以上"
),
SettingsJavaDefaultSet => ("Default JDK is now {path}", "默认 JDK 已设为 {path}"),
SettingsJavaSourceManaged => ("installed by this launcher", "本启动器安装"),
SettingsJavaSourceEnv => ("JAVA_HOME (environment variable)", "JAVA_HOME(环境变量)"),
SettingsJavaSourcePath => ("on PATH", "PATH 上"),
SettingsJavaSourceSystem => ("system install", "系统安装"),
SettingsJavaSourceManual => ("added by you", "手动添加"),
// ── 手动添加
SettingsJavaAdd => ("Add a JDK path", "手动添加 JDK 路径"),
SettingsJavaAddPlaceholder => (
"C:\\Program Files\\Java\\jdk-21",
"例:C:\\Program Files\\Java\\jdk-21"
),
SettingsJavaAddHint => (
"A JDK folder or its java executable. We run `java -version` on it; anything that cannot answer is refused.",
"JDK 目录或它的 java 可执行文件。会真跑一次 `java -version`;答不出话的一律拒绝。"
),
SettingsJavaAddAction => ("Add", "添加"),
SettingsJavaAdded => ("Added {path}", "已添加 {path}"),
// ── 内存
SettingsJavaMemory => ("Default memory (MB)", "默认内存(MB)"),
SettingsJavaMemoryHint => (
"Whole megabytes, {min}–{max}. Instances without their own override follow this value.",
"整数 MB,范围 {min}~{max}。没有实例级覆盖的实例都用它。"
),
SettingsJavaLayerGlobal => (
"In effect: {mb} MB, from the global default.",
"生效:{mb} MB,来自全局默认值。"
),
SettingsJavaLayerInstance => (
"In effect: {mb} MB, set by instance {instance}. Changing the global value below will not affect that instance.",
"生效:{mb} MB,由实例 {instance} 覆盖。改下面的全局值不会影响它。"
),
// ── JVM 参数
SettingsJavaJvm => ("Extra JVM arguments", "额外 JVM 参数"),
SettingsJavaJvmPlaceholder => ("-XX:+UseG1GC -Dfile.encoding=UTF-8", "例:-XX:+UseG1GC -Dfile.encoding=UTF-8"),
SettingsJavaJvmHint => (
"Separated by spaces, quotes allowed. Appended to the command line of every launch; an instance's own JVM arguments come after it.",
"按空白分隔,支持引号。会拼到每次启动的命令行上;实例自己的 JVM 参数排在它后面。"
),
SettingsJavaJvmSaved => ("Saved value: {value}", "已保存的值:{value}"),
SettingsJavaJvmSavedNone => (
"No extra JVM arguments are saved yet.",
"还没有保存任何额外参数。"
),
// ── 保存与回执
SettingsJavaSave => ("Save", "保存"),
SettingsJavaSaved => (
"Saved and read back from disk. The next launch uses the new value.",
"已保存,并从磁盘读回确认。下次启动就用新值。"
),
SettingsJavaUnsaved => ("There are unsaved changes.", "有未保存的改动。"),
SettingsJavaRefused => ("Not saved: {reason}", "未保存:{reason}"),
SettingsJavaSkipNotDirty => (
"there was nothing to change, so no file was written",
"没有任何改动,因此没有写文件"
),
SettingsJavaSkipReadOnly => (
"the settings file is read-only: a newer launcher wrote it, or it is marked untrusted",
"配置文件只读:它被更新版本的启动器写过,或被标记为不可信"
),
SettingsJavaSkipUnreadable => (
"the settings file is untrusted, so it is treated as read-only",
"配置文件不可信,因此按只读处理"
),
SettingsJavaNoStorage => (
"No settings file is available in this run, so nothing can be scanned or saved.",
"这一次运行没有可用的设置文件,扫描与保存都做不了。"
),
// ── 拒绝原因(一条一句)
SettingsJavaReasonMemoryEmpty => ("the memory field is empty", "内存没填"),
SettingsJavaReasonMemoryNotNumber => ("the memory must be a whole number of MB", "内存必须是整数 MB"),
SettingsJavaReasonMemoryRange => (
"the memory is outside the allowed range",
"内存超出允许范围"
),
SettingsJavaReasonJvmQuotes => (
"the JVM arguments have an unclosed quote",
"JVM 参数里有没配对的引号"
),
SettingsJavaReasonAddEmpty => ("the path is empty", "路径是空的"),
SettingsJavaReasonAddMissing => ("nothing exists at that path", "那个路径上什么都没有"),
SettingsJavaReasonAddNotJava => (
"there is no java executable at that path",
"那个路径里没有 java 可执行文件"
),
SettingsJavaReasonAddNoVersion => (
"java -version did not answer there, so it is not a usable JDK",
"在那里跑不出 java -version,不是一份能用的 JDK"
),
SettingsJavaReasonAddTooLong => (
"the path is too long to be a JDK location",
"路径太长,不像是一个 JDK 位置"
),
SettingsJavaReasonAddWriteFailed => (
"the JDK is fine, but the list file could not be written (disk or permission)",
"这份 JDK 没问题,但名单文件写不进去(磁盘或权限)"
),
SettingsJavaReasonAddDuplicate => ("that JDK is already in the list", "这份 JDK 已经在列表里了"),
SettingsJavaReasonWriteFailed => (
"the settings file could not be written (disk full, no permission, or the value was rejected)",
"设置写不进去(磁盘满、没权限,或这个值被拒绝)"
),
} }
};
}