首页
/ Windows Terminal Actions Page 设计规格:设置 UI 如何表达键盘快捷键与命令

Windows Terminal Actions Page 设计规格:设置 UI 如何表达键盘快捷键与命令

2026-09-06 11:59:35作者:戚魁泉Nursing

本文基于仓库内的设计规格文档 [doc/specs/#6900 - Actions Page/spec.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/spec.md),讲解 Windows Terminal 设置编辑器中 Actions(操作)页面的设计动机、两个候选方案及其取舍结论,并结合 TerminalSettingsEditor 的实际源码,还原从"规格设计"到"MVVM 实现"的完整链路。读完后,你将理解为什么设置 UI 最终采用"Keyboard/Actions 页 + 未来 Command Palette 页"的分页策略,以及当前 Actions 页在命令列表构建、按键冲突处理、in-box 命令复制等方面的实现原理。

![新增快捷键绑定的界面](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/add-keys.png)

![编辑快捷键绑定的界面](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/edit-keys.png)

背景:为什么设置 UI 需要专门的 Actions 页

规格的出发点是一个明确的需求:在设置 UI 中表达 actions(可绑定的操作)。原文的 Abstract 写道:

We need to represent actions inside the settings UI. This spec goes through the possible use cases and reasoning for including specific features for actions inside the settings UI.

理想情况是设置 UI 与 JSON 配置文件(settings.jsonactionskeyBindings)保持完全对等(parity),但这会带来大量设计工作量;另一个选择是放弃完全对等,用更简单的 UX 呈现。规格随后列出了 JSON 文件中全部可实现的用户故事,作为 UI 设计的对照清单:

  1. 为一个尚无按键绑定的 action 添加快捷键
  2. 编辑某个 action 的快捷键
  3. 从某个 action 上移除快捷键
  4. 为同一个 action 添加多个快捷键绑定
  5. 创建可迭代的 action(iterable action)
  6. 创建嵌套 action(nested action)
  7. 选择哪些 action 出现在命令面板(command palette)中
  8. 查看所有可能的 action,无论其是否分配了按键

规格还特别列出了带属性(properties)的命令,这些属性是 UI 设计时无法回避的复杂度来源:

命令 属性
sendInput input
closeOtherTabs index
closeTabsAfter index
renameTab title*
setTabColor color*
newWindow commandlinestartingDirectorytabTitleindexprofile
splitPane splitcommandlinestartingDirectorytabTitleindexprofilesplitModesize
copy singleLinecopyFormatting
scrollUp rowsToScroll
scrollDown rowsToScroll
setColorScheme colorScheme

(带 * 的属性在规格中标注为后来补充。)规格指出:上表中的大多数命令本就面向命令面板,因此给它们分配按键的意义不大。此外,规格在 Future Considerations 中预留了两种未来入口:未来下拉菜单项可触发 action(该设置需要有个存放位置),以及状态栏出现后用户可能希望从状态栏调用 action。

两个候选方案与最终结论

方案一:Keyboard 页 + (未来的)Command Palette 页

用 Keyboard 页替代 Actions 页;如果呼声足够高,未来再规划 Command Palette 页来覆盖缺少的用例。关键交互设计:

  • 用户想新增快捷键绑定时,下拉框列出所有 action,无论其是否已有按键;
  • 该页展示每个 action 上已分配的全部按键绑定,即使同一 action 有多组绑定也逐条列出;
  • 用户如需浏览所有可能的 action,可从命令面板进入。

覆盖的用例

  • 为尚无按键的 action 添加快捷键
  • 编辑某 action 的快捷键
  • 移除某 action 的快捷键
  • 为同一 action 添加多组快捷键
  • 查看所有已分配按键的 action

未覆盖的用例:创建 iterable/nested action、选择命令面板中显示的 action、查看所有 action(含无按键者)。

优点:覆盖大多数编辑场景;给团队留出观察空间——先看用户对未覆盖用例的真实需求再决定投入。缺点:并非所有用例都能覆盖;部分命令的属性(properties)无法编辑——不过规格认为这可以接受,因为命令面板内置的默认命令本身就带着属性,例如 "decrease font size" 已内置 delta 属性。

方案二:一个 Actions 页承载一切

一个 Actions 页同时允许创建命令面板 action 与带按键的 action。覆盖全部 8 项用例,与 JSON 完全对等。优点只有"与 JSON 完全对等"一条;缺点是规格作者直言:"I could not come up with a UX design that wasn't too complicated or confusing for this scenario."——设计迅速变得臃肿,违背设置 UI 推崇易用性的初衷。

结论

规格的最终结论是:考虑过方案二,但设计很快变得杂乱(cluttered),因此团队一致决定做两个页面,先落地方案一

UI/UX 设计:规格中的界面草图

规格附了四张界面草图,展示了 Add/Edit 两个核心交互的完整链路:

草图 说明
[add-click.png](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/add-click.png) 点击"新增"按钮后的界面状态
[add-keys.png](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/add-keys.png) 新增快捷键绑定时的按键录入
[edit-click.png](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/edit-click.png) 点击某条绑定的编辑入口
[edit-keys.png](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/edit-keys.png) 编辑现有快捷键绑定

其中一条设计细节值得注意:规格明确写道,"Add new" 按钮使用次要颜色(secondary color),以与 Color schemes 页上的同名按钮保持视觉对齐。

从规格到实现:Actions 页的 MVVM 架构

当前仓库中,设置编辑器的 Actions 页由 ActionsViewModel.h 定义。文件头部注释完整描述了四层视图模型的职责划分,与规格的交互设计一一对应:

  • ActionsViewModel:持有"当前子页"枚举(顶层 Actions 页 / EditAction 页)、完整命令列表与当前正在编辑的命令,负责命令的增删,并监听各 CommandViewModel 的按键组合(key chord)事件;
  • CommandViewModel:由 Model::Command 对象构造,是列表中每一项的视图模型,包含命令名称、是否为 user command、快捷键 action 类型等高层信息,并按 action 类型创建对应的 ActionArgsViewModel;
  • ActionArgsViewModel / ArgWrapper:负责各参数的绑定与回写(bind back)逻辑,按参数类型区分呈现方式;
  • KeyChordViewModel:由 Control::KeyChord 构造,处理单条快捷键在 UI 中的录入、修改与删除。

命令列表的构建

Actions 页列表并非直接读 JSON,而是从设置模型的 ActionMap 聚合而来。在 ActionsViewModel.cpp_MakeCommandVMsHelper 中可以看到核心逻辑:遍历 ActionMap().AllCommands(),对每条命令用 AllKeyBindingsForAction(cmd.ID()) 取出它的全部按键绑定(对应规格中"同一 action 多组绑定逐条列出"的要求),按显示名排序后生成 ObservableVector。这也解释了规格中"该页不 1:1 对应 JSON"的取舍在实现里的落点:列表项以"命令 + 其全部按键"为单位呈现。

值得注意的是,列表构建时会过滤掉 UnimplementedShortcutActions 中的 action——目前包括 MultipleActions(即规格中"nested action"对应的 multiple actions)与 ColorSelection。源码注释(TODO: GH 19056)说明这两类参数的 UI 绑定尚未实现,因此这些内置命令暂不出现在新操作编辑器中——这正是规格"未覆盖用例"在实现层的直接体现。

新增命令

点击页面上的 Add new 按钮触发 AddNewCommand:创建一个 Model::Command::NewUserCommand()(即 Command.cpp 中标记 OriginTag::User 的新命令),取可用 action 列表的第一项、用 ActionArgFactory::GetEmptyArgsForAction 填充空参数,随后加入 ActionMap、生成视图模型并跳转到 Edit 子页。注意这里创建出的命令可以不配任何按键——实现实际上超出了规格"潜在问题"一节中"当前设计下无法新增无按键 action"的限制,因为 Edit 子页允许直接编辑 action 类型与参数。

按键冲突的检测与确认弹窗

规格强调"编辑/删除/新增绑定"是核心用例,而多组绑定必然引出冲突问题。AttemptAddOrModifyKeyChord 展示了完整的处理链:

  1. 先调 ActionMap().GetActionByKeyChord(newKeys) 检查新按键是否已被占用;
  2. 若存在冲突命令,弹出 Flyout 确认对话框:显示冲突提示文本、冲突命令名称(无名命令时回退为"未命名命令"的本地化文本)、确认问题与"接受覆盖"按钮——对应资源键 Actions_RenameConflictConfirmationMessage 等;
  3. 用户接受后(或无冲突时),applyChangesToSettingsModelDeleteKeyBinding(oldKeys)AddKeyBinding(newKeys, commandID),实现"改键 = 删旧 + 加新",随后收起 Flyout 并退出按键录入的编辑模式。

按键录入本身由 KeyChordListener 控件承担:它监听 KeyDown 事件并把按键组合写回 Keys 依赖属性,这正是规格草图中"点击后直接敲键"交互的实现。

in-box 命令的"写时复制"

规格只讨论用户可编辑的 JSON 命令,而当前实现要处理一个更微妙的问题:编辑内置(in-box)命令时怎么办CommandViewModel 的源码注释给出了答案:当用户修改一条 in-box action 的参数或 action 类型时,_ReplaceCommandWithUserCopy 会调用 Command::CopyAsUserCommand 生成一份带相同 ID、但 Origin 标记为 OriginTag::User 的副本,并通过 AddCopiedCommand 写入 ActionMap。IsUserAction() 则通过 _command.Origin() == OriginTag::User 判定。从源码结构看,这是"设置 UI 与 JSON 对等"目标的一个工程化落地:内置命令保持只读,用户改动以覆盖形式落为用户自己的命令副本。

已知限制、取舍与后续演进

规格在 Potential Issues 一节如实记录了三个限制,可以作为理解当前实现边界的基线:

  1. 该设计与 JSON 不是 1:1:没有按键的 action 不会出现在页面上(此限制在后续实现中已被放宽——用户命令可以无按键,见上文 AddNewCommand);
  2. 无法通过 UI 给命令指定属性(如 newTab 的属性),只能走 JSON 文件。规格的判断是"这样的命令很少,且我们计划迭代出 Command Palette 页,所以可以接受"。从当前源码结构看,Edit 子页已经能通过 ArgWrapper/ActionArgsViewModel 编辑 splitindex 等参数,属于对这一限制的持续迭代;
  3. 嵌套(multiple)action 与 ColorSelection 暂不可编辑,对应上面提到的 UnimplementedShortcutActions 过滤清单,相关 TODO 指向问题单 GH 19056。

规格中"未来考虑"的另外两个方向(下拉菜单触发 action、状态栏调用 action)在仓库的 设置模型规格 及 Action IDs 相关规格(见 [doc/specs/#6899 - Action IDs/#6899 - Action IDs.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6899 - Action IDs/#6899 - Action IDs.md))中有延续讨论。

相关源码与文档索引

路径 说明
[doc/specs/#6900 - Actions Page/spec.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/spec.md) 本文主体设计规格(作者 Kayla Cinnamon,2021-03)
src/cascadia/TerminalSettingsEditor/ActionsViewModel.h Actions 页四层视图模型定义
src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp 命令列表构建、新增命令、按键冲突处理等实现
src/cascadia/TerminalSettingsEditor/Actions.xaml Actions 页 XAML:Add new 按钮、命令列表模板
src/cascadia/TerminalSettingsEditor/EditAction.xaml Edit 子页:编辑 action 类型、名称与参数
src/cascadia/TerminalSettingsEditor/KeyChordListener.h 按键组合录入控件
src/cascadia/TerminalSettingsModel/Command.cpp NewUserCommand / CopyAsUserCommand 数据模型实现

适用前提:以上分析基于当前仓库源码,针对的是 Windows Terminal 设置编辑器(CascadiaSettings / TerminalSettingsEditor 组件)中的 Actions 页;规格文档写于 2021 年 3 月,实现细节(如无按键用户命令、参数编辑、写时复制)是规格结论之后的演进,文中已逐处标注出处,引用时请以源码为准。

登录后查看全文
热门项目推荐
相关项目推荐