@@ -96,3 +96,117 @@ xmake b qml_load_test && xmake r qml_load_test
9696 ` ("this change requires ...)" ...) ` ×2 / ` ("keyboard shortcut style" "快捷键显示风格") ` ,
9797 全部** 小写首字母登记** (靠 ` translate ` 首字母大写折叠命中代码里的大写 key)
9898- 按** 字母序** 插入 ` zh_CN.scm ` 对应字母段
99+
100+ ## 8 补充:切换语言接入 ConfirmRestart 三按钮 + later 统一 silent 写值
101+
102+ 2040 合并后,原计划用独立 PR(2041)把切换语言改成不弹窗即时生效,但 2041 暂无法合并。
103+ 而 language 切换确实需要重启才生效,故将其从旧 ` user-confirm ` (两按钮)升级为
104+ ConfirmRestart 三按钮,与 look and feel / gui theme 等需重启字段统一。
105+
106+ ### What
107+ 1 . ` set-language-and-notify ` :值变化时改走 ` confirm-restart-and-act ` (重启/稍后/取消),
108+ 值未变直接落定不弹窗
109+ 2 . ` confirm-restart-and-act ` 加第 4 参 ` later-proc ` (固定 4 参,非可选——tm-define 下
110+ rest 参数虽可用但固定签名更清晰),later 分支调它
111+ 3 . ** 新增 C++ glue ` cpp-set-preference-silent ` ** (绑 ` set_user_preference_silent ` ):
112+ 写 preference 值但** 不调 ` notify_preference ` ** 。later-proc 用它写值,避免当前会话
113+ 实时切(实时切换需重启字段可能 crash 或状态不一致),下次启动读 preference 生效
114+ 4 . ** 所有需重启字段 later 统一 silent 写值** (不只 language):
115+ - ` set-pretty-preference* ` (look and feel / gui theme):later-proc 用新增的
116+ ` set-pretty-preference-silent ` (decode + ` cpp-set-preference-silent ` + 存盘)
117+ - ` set-language-and-notify ` :later-proc 用 ` cpp-set-preference-silent ` + 存盘
118+ 5 . ` restart-preference-title ` 从 ` preferences-widgets.scm ` ** 移到 ` preferences-menu.scm ` ** ,
119+ 新增 ` language ` 分支(` translate "Switch language" ` ),改 ` tm-define ` 导出
120+ (原 widgets 私有 ` define ` 形成 widgets→menu 反向依赖)
121+ 6 . 删除被取代的 ` restart-required-message ` (旧两按钮正文,已无调用方)及字典条目
122+ ` Requires restarting ... Restart now? ` ×2(死 key)
123+ 7 . 字典新增 ` ("switch language" "切换语言") `
124+
125+ ### Why
126+ - 三按钮 later 语义统一为「写新值,下次启动生效」——当前会话不实时切。
127+ ** 根因** :` cpp-set-preference ` (C++ ` set_user_preference ` )内部会调 ` notify_preference ` ,
128+ 触发 ` notify-language ` 等回调实时切换;实时切 language 不重启可能 crash。故新增
129+ ` set_user_preference_silent ` (只写值不 notify),later 走它。look and feel / gui theme
130+ 一并统一,行为一致且避免各自实时副作用
131+ - restart 仍用 ` set-preference ` (notify,实时切)+ 存盘 + 重启——马上重启,实时副作用无所谓
132+ - ` restart-preference-title ` 移到 menu:消除 widgets→menu 反向依赖,menu/widgets 共用
133+ - 删 ` restart-required-message ` :升级后无调用方,连配套字典 key 一起清理
134+
135+ ### How
136+ - ` src/System/Config/preferences.cpp ` / ` .hpp ` :新增 ` set_user_preference_silent `
137+ (同 ` set_user_preference ` 但去掉 ` notify_preference(var) ` )
138+ - ` src/Scheme/Glue/glue_basic.lua ` + ` TeXmacs/progs/prog/glue-symbols.scm ` :声明并登记
139+ ` cpp-set-preference-silent `
140+ - ` TeXmacs/progs/kernel/texmacs/tm-preferences.scm ` :新增 ` set-pretty-preference-silent `
141+ (decode + silent 写值 + 存盘)
142+ - ` preferences-menu.scm ` :
143+ - ` restart-preference-title ` (tm-define,含 language 分支)
144+ - ` confirm-restart-and-act ` 固定 4 参 ` (title apply-proc rollback-proc later-proc) ` ,
145+ later 走 ` later-proc `
146+ - ` set-language-and-notify ` 传 later-proc = ` cpp-set-preference-silent + save-preferences `
147+ - ` preferences-widgets.scm ` :` set-pretty-preference* ` later-proc 用 ` set-pretty-preference-silent ` ;
148+ 删本地 ` restart-preference-title `
149+ - ` zh_CN.scm ` :删 ` Requires restarting ... Restart now? ` ×2,加 ` switch language `
150+
151+ ### 验证
152+ ``` bash
153+ gf fmt --changed-since=main
154+ xmake b stem && xmake i stem # 注意:glue 改动需 xmake i stem 更新打包目录
155+ xmake b qml_load_test && xmake r qml_load_test # ConfirmRestart 加载测试
156+ # language 三按钮(用 MOGAN_TEST_CONFIRM_RESTART 钩子驱动,headless 可跑):
157+ MOGAN_TEST_CONFIRM_RESTART=later xmake r 2040 # later 写值不实时切(get-output-language 不变)
158+ # 人工:打开首选项切 Language / gui theme:
159+ # 重启 = 立即生效并重启
160+ # 稍后 = 当前会话不变,下次启动生效(preference 已存新值)
161+ # 取消 = 回滚
162+ ```
163+
164+ ## 9 preference 写值的 notify 机制与 silent 变体
165+
166+ 本 PR 新增 ` set_user_preference_silent ` (写值不 notify),服务于「稍后」分支。记录
167+ notify 机制,说明 silent 的适用边界。
168+
169+ ### 9.1 notify 是「同步进程内派生状态」
170+
171+ ` notify_preference ` (C++ ` preferences.cpp:188 ` → scheme ` notify-preference ` ,
172+ ` tm-preferences.scm:103 ` )按字段名分派到 call-back:
173+
174+ ``` scheme
175+ (tm-define (notify-preference which)
176+ ((get-call-back which) which (get-preference which)))
177+ ```
178+
179+ call-back 的职责是** 让进程内的派生状态跟上 preference 新值** 。以 ` language ` 为例,
180+ call-back ` notify-language ` (` tm-server.scm:51 ` )调 ` set-output-language ` ,改 scheme
181+ 全局 ` output_language ` ——这是「当前生效值」,独立于 preference 文件里的值。
182+
183+ ### 9.2 两类字段,silent 的适用面不同
184+
185+ - ** 纯存储型** (如 ` plugin:binary:* ` 、` texlive:* ` 路径):无 call-back 维护派生状态,
186+ 读写只看 preference 值。这类字段 silent 写值无副作用。
187+ - ** 带派生状态型** (language / gui theme / look and feel):进程内有靠 notify 同步的
188+ 「当前生效值」。silent 写值后 preference 已更新,但生效值未变,二者短暂脱节。
189+
190+ 脱节在本 PR 是** 预期的** :用户选「稍后」即明知当前会话不变、下次启动生效;脱节状态寿命
191+ 仅到下次重启。因此 silent 是有适用前提的特化写值(需当前会话能容忍脱节),并非通用 setter。
192+ ` .hpp ` 的 ` @note ` 标注此约束,避免日后误用于「想立即生效却忘了 notify」的普通字段。
193+
194+ ### 9.3 应用:本 PR 的 silent 写值与 restart 分支
195+
196+ - ** ` set_user_preference_silent ` ** (later 分支):写新值落盘,下次启动生效,当前会话不
197+ 实时切。language 实时切不重启可能 crash(devel 记录的根因),later 用 silent 规避。
198+ - ** restart 分支仍走 ` set-preference ` (notify)** :紧接 ` save-all-buffers ` +
199+ ` restart-TeXmacs ` ,进程即将退出,实时切与退出间窗口极短,无需 silent。
200+ - silent 调用点目前仅 2 处(language later-proc / ` set-pretty-preference-silent ` ),
201+ 均为受控的 later 场景。
202+
203+ ### 9.4 顺带的注释/测试补充
204+
205+ - ` preferences.hpp ` :silent 声明补 Doxygen ` @note ` (标注适用边界)
206+ - ` preferences.cpp ` :silent 函数体补一行 Why
207+ - ` preferences-menu.scm ` ` set-language-and-notify ` :later / restart 两分支各一行 Why,
208+ later-proc 内联注释说明 language 无 pretty 映射、不复用带 decode 的
209+ ` set-pretty-preference-silent `
210+ - ` tests/2040.scm ` :头部注明 later 落盘未断言(scheme 侧无 preference 文件路径入口),
211+ 误删 later-proc 的 ` save-preferences ` 此测试不会失败;后续可补 glue 读文件验证
212+
0 commit comments