桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载导读本文围绕 MooTool 下一代跨平台版本next-compose中 JSON 工具模块的JsonPathPickerDialog弹层重构展开为兼顾可测试性与「使用此路径」仅选路径、不自动查询的交互语义DIFF-319项目将原本内联在JsonScreen中的路径选择对话框提取为独立 Compose 组件并通过语义化contentDescription、会话状态封装与桌面 Compose UI 测试把「使用」与「双击选路径」两条行为链路固化为可回归的自动化用例。读完本文你将掌握该组件从提取、状态驱动、无障碍对齐到交互单测与场景帧证据的完整实现思路可直接迁移到自身 Compose 桌面项目的弹层测试与行为封装中。一、背景为什么要把PathPickerDialog从JsonScreen中提取出来在重构之前JSONPath 选择弹层PathPickerDialog是直接内联在JsonScreen里的。随着功能演进它暴露出两个问题体量过大不利于单测弹层既承担路径列表加载、选中态维护又要处理预览与确认/取消逻辑内联在 2200 行的JsonScreen中见 JsonScreen.kt很难被 Compose 测试独立挂载和驱动交互语义需要回归保护Electron 版JsonPathPicker的「使用」与双击共用onChoose(path)——只把路径写入输入框并关闭弹层不触发 JSONPath 查询、不把节点预览写进结果区。Compose 端若在此处行为漂移会导致 DIFF-319「仅选路径、不自动查询」的回归。因此 DIFF-457 的核心动作是把弹层提取为独立组件让回归验证落到可重复执行的单测上。二、组件提取JsonPathPickerDialog的结构与状态驱动2.1 新增独立组件文件新增 JsonPathPickerDialog.kt对外暴露一个internal的 ComposableComposable internal fun JsonPathPickerDialog(container: AppContainer, session: JsonSession, onChanged: () - Unit)三个入参各有明确职责参数类型职责containerAppContainer提供 i18n 翻译container.t(key, params)与全局能力弹层内所有文案统一走翻译键sessionJsonSession承载编辑区文本、jsonPath、pathPickerOpen、pathPickerSelection、pathResult、notice等会话状态是弹层的唯一数据源onChanged() - Unit状态变更后的通知回调由JsonScreen传入{ refresh() }驱动界面刷新调用侧由JsonScreen在pathPickerOpen为 true 时挂载见 JsonScreen.ktif (session.pathPickerOpen) { JsonPathPickerDialog(container, session) { refresh() } }2.2 路径列表的懒加载与兜底弹层组合时并不会预先持有完整路径树而是基于当前编辑器文本即时计算val entries runCatching { JsonEngine.listPaths(session.editor.text, translator) }.getOrDefault(emptyList()) val entryPaths entries.map { it.path }解析失败的场景编辑器里不是合法 JSON通过runCatching兜底为空列表弹层表现为无条目可选不会崩溃路径枚举逻辑在 JsonEngine.kt 的listPaths中实现核心是collectPaths的递归遍历JsonEngine.kt数组节点生成$path[index]形式label 为[index]对象节点键名若匹配^[a-zA-Z_$][\w$]*$则生成$path.key否则生成$path[键名]带 JSON 引号每个节点产出JsonPathEntry(path, label, preview(node), depth)四元组模型定义见 JsonModels.kt。2.3 打开弹层时自动对齐当前选中路径弹层通过LaunchedEffect在打开时把选中项对齐到会话当前jsonPath避免旧pathPickerSelection残留LaunchedEffect(session.pathPickerOpen, session.jsonPath, entryPaths) { if (!session.pathPickerOpen) returnLaunchedEffect val initial jsonPathPickerSelectionForOpen(session.jsonPath, entryPaths) if (session.pathPickerSelection ! initial) { session.pathPickerSelection initial onChanged() } }对齐规则实现在 JsonInspectorPathUi.kt 与 JsonInspectorResult.ktinternal fun jsonPathPickerInitialSelection(jsonPath: String, entryPaths: ListString): String entryPaths.find { it jsonPath } ?: entryPaths.firstOrNull() ?: jsonPath优先级当前jsonPath存在于路径树 → 选中它不存在则选中第一个路径条目连条目都没有则原样保留jsonPath。注意此时只改选中态不写入、不查询。2.4 布局与预览区弹层尺寸固定为 760×480 dp主题化mooDialogSurface圆角卡片内部为左右两栏见 JsonPathPickerDialog.kt左栏weight(0.9f)LazyColumn渲染JsonPathListRow条目单击仅更新pathPickerSelection双击走applyPathPickerChoice双击语义见下文 DIFF-458 部分中缝1 dp 分隔线右侧预览区用 1 dp 边框线隔离右栏weight(1.1f)展示选中路径等宽字体、11 sp与节点预览等宽字体、11 sp预览文本通过jsonPathNodePreview懒计算并随editor.revision与预览路径变化而重建val previewText remember(session.editor.revision, previewPath) { jsonPathNodePreview(session.editor.text, previewPath, translator, current?.preview.orEmpty()) }预览格式化由 JsonEngine.kt 的pathPickerPreview提供对齐 ElectronJsonPathPicker的formatPreview字符串节点返回原文不带 JSON 引号对象/数组节点走 pretty-print路径失效或缺失时返回空字符串。三、「使用」按钮语义化contentDescription与「仅选路径」行为3.1 无障碍语义对齐「使用」按钮通过Modifier.semantics显式声明contentDescriptionJsonPathPickerDialog.ktmodifier Modifier.semantics { contentDescription container.t(json.pathPicker.use) }文案键json.pathPicker.use在 Translator.kt 中登记为中文「使用该路径」。这一设计同时服务两类消费者Compose UI 测试测试可用onNodeWithContentDescription(zh.t(json.pathPicker.use))精确点击按钮无障碍与同窗口其他可点击控件的语义保持一致屏幕阅读器可朗读出按钮含义。按钮可用性由canUse current ! null决定——只有选中项确实存在于路径树中时「使用」才可点击有效拦截选中了一个不存在路径的非法提交。相关弹层内 i18n 键还有json.pathPicker.title选择 JSONPath、json.pathPicker.path路径、json.pathPicker.preview预览。3.2 点击后的状态变换applyPathPickerChoice「使用」按钮点击后调用会话级的统一行为封装JsonInspectorResult.ktinternal fun JsonSession.applyPathPickerChoice(path: String, pathAppliedNotice: String) { jsonPath path clearJsonPathQueryResult() notice pathAppliedNotice pathPickerOpen false }这一步完成了 DIFF-319 强调的 Electron 对齐语义状态字段变更结果含义jsonPath写入选中路径路径输入框反映新选择pathResult清空不触发 JSONPath 查询也不把节点预览塞进结果区notice置为json.notice.pathApplied中文「已应用 JSON Path」仅以提示形式告知用户路径已应用pathPickerOpen置为 false弹层随即关闭与之形成对照的是检查器内联路径树的单机预览行为applyInlinePathTreePreviewJsonInspectorResult.kt会写入jsonPath并把预览写入pathResult。也就是说弹层「使用」走的是纯净选择语义内联树单击走的是预览语义两者刻意区分由 DIFF-319 与 DIFF-457 共同固化。四、交互单测useButtonAppliesPathWithoutQuery的完整链路4.1 测试骨架新增 JsonPathPickerDialogInteractionTest.kt以runDesktopComposeUiTest(width 800, height 520)挂载真实MooTheme与对话框用createTempDirectory构造临时产品目录走AppPaths.resolve(...).ensureCreated()创建SettingsRepository、AppDatabase、HistoryRepository、LegacyMigrationRowRepository、SessionStore最终组装出与生产等价的AppContainersession直接以JsonSession()构造并预置状态session.editor.setText({store:{books:[{title:One}]}})固定测试 JSONsession.pathPickerOpen true打开弹层session.pathPickerSelection $.store.books[0].title预置选中路径session.pathResult [stale]预置一条过期查询结果用于验证使用后必须被清空。4.2 断言矩阵waitForIdle() onNodeWithContentDescription(zh.t(json.pathPicker.use)).performClick() assertFalse(session.pathPickerOpen) assertEquals($.store.books[0].title, session.jsonPath) assertEquals(, session.pathResult) assertEquals(zh.t(json.notice.pathApplied), session.notice)这条测试把 DIFF-319 的行为契约翻译成 5 个显式断言弹层关闭pathPickerOpen false路径写入jsonPath $.store.books[0].title查询结果被清空pathResult 即不自动查询的直接证据——如果弹层「使用」触发了查询预置的[stale]会被新结果覆盖而非清空提示为「已应用 JSON Path」测试通过后deleteRecursively()清理临时目录。从源码调用链看测试点击按钮后实际执行的是 JsonPathPickerDialog.kt 的onClick其中val entry current ?: returnMooButton的守卫与canUse共同保证非法状态不可提交。4.3 双击路径行的补充回归DIFF-458DIFF-458 在弹层单测中追加了双击链路doubleTapRowAppliesPathWithoutQuery通过onNodeWithContentDescription($.store.books[0].title).performTouchInput { doubleClick() }触发断言与「使用」完全一致关闭弹层、写入路径、清空pathResult、仅设置pathApplied提示。其实现基础是 JsonPathListUi.kt 中JsonPathListRow的.semantics(mergeDescendants true) { contentDescription entry.path } .pointerInput(entry.path) { detectTapGestures(onTap { onTap() }, onDoubleTap { onDoubleTap() }) }mergeDescendants true保证整行路径作为单一语义节点暴露给测试与辅助功能detectTapGestures同时承载单击仅改选中与双击应用路径两种手势。至此弹层的「使用」与「双击」两条入口共用同一applyPathPickerChoice语义与 ElectrononChoose(path)完全对齐。五、场景帧捕获capturePathPickerUseButtonFocusRing除行为断言外DIFF-457 还新增了 JsonPathPickerCaptureTest.kt 用于产出 Compose 场景帧证据同样以runDesktopComposeUiTest(width 800, height 520)挂载弹层{a:1,b:2}简单 JSONwaitForIdle()后onRoot().captureToImage().toAwtImage()抓取整帧写盘路径为docs/evidence/2026-09-15-inspector-screencapture/windows/148-compose-json-path-picker-use-tab-focus.png先mkdirs()再ImageIO.write断言file.length() 3_000防止写出空白/损坏帧。该证据图片同时记录了「使用」按钮在 Tab 焦点下的焦点环渲染属于对非产品主窗的独立 Compose 场景帧供 UI 评审与回归对照六、验证命令与回归结果在next-compose目录下执行桌面测试全量验证./gradlew :composeApp:desktopTest --offlineDIFF-457 落地时全量桌面测试通过615/615随后 DIFF-458 追加双击用例后扩展至 616/616。--offline表示不访问远程依赖仓库、纯本地离线构建适合在 CI 受限或网络隔离环境下稳定复现。七、设计要点小结弹层组件化是可测试性的前提JsonPathPickerDialog(container, session, onChanged)的签名让测试能以真实AppContainer 手工构造的JsonSession独立挂载弹层而不必启动整个JsonScreen状态封装收敛行为契约applyPathPickerChoice把仅选路径、不查询、关闭弹层、置提示四项语义收敛为单一函数使「使用」与「双击」天然复用同一契约杜绝分支漂移语义化贯穿 UI 与测试contentDescription同时服务onNodeWithContentDescription定位与无障碍朗读是 Compose 桌面端可测性即可访问性的典型实践证据帧沉淀 UI 回归场景帧捕获测试把无法用断言表达的视觉细节焦点环、布局落盘为证据文件供跨平台对照与评审追溯。如需继续深入可进一步阅读同目录下的相关 DIFF 记录DIFF-319「仅选路径、不自动查询」语义、DIFF-458 双击路径行回归以及弹层依赖的核心实现 JsonEngine.listPaths 与 JsonInspectorResult。赞分享桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载相关推荐MooTool Compose 桌面版 JSONPath 选择器弹层对齐 Electron 布局规格解析与实现验证MooTool Compose 桌面版 JSONPath 选择器弹层对齐 Electron 布局规格解析与实现验证 MooTool 在 Electron 前端桌面应用开发工具Penrose 词云示例实战用 Style 约束与目标函数排版文本标签Penrose 词云示例实战用 Style 约束与目标函数排版文本标签 本指南以仓库中 word cloud 示例 https://link.gitcode.桌面应用开发工具MooTool Compose 版 JSONPath 弹层双击执行查询的设计演变从 DIFF-285 到 DIFF-319 的交互收敛MooTool Compose 版 JSONPath 弹层双击执行查询的设计演变从 DIFF 285 到 DIFF 319 的交互收敛 导读 本文围绕 Moo桌面应用开发工具上一篇DDrawCompat终极指南让经典DirectX游戏在现代Windows上完美运行下一篇一键解决Windows DLL缺失问题Visual C运行库完整安装指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考