首页
/ Windows Terminal 设置 UI 设计解析:导航结构、外观预览窗口与级联继承在源码中的落地

Windows Terminal 设置 UI 设计解析:导航结构、外观预览窗口与级联继承在源码中的落地

2026-09-04 09:30:11作者:魏献源Searcher

本文围绕 [设计文档](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/design.md)(issue #1564 的 Settings UI Design)展开,讲清楚 Windows Terminal 设置界面的页面导航布局、各页面的完整设置项清单、带实时预览窗口的 Appearance 页设计以及键盘绑定编辑弹窗形态,并结合仓库中 TerminalSettingsEditorTerminalSettingsModel 的实际源码,印证这些设计如何演化为今天可以运行的设置 UI——读完后你既能理解设计稿中每个页面与控件的意图,也能定位到实现它的 XAML、导航标签与继承机制代码。

![Settings UI 整体导航结构](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/navigation-2.png)

一、设计文档的定位:从 issue #1564 到三份配套规格

[design.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/design.md) 的文档头元数据表明,它由 Kayla Cinnamon 于 2020-07-13 创建、2020-08-11 最后更新,对应 issue #1564。文档摘要明确了两点:

  • 它描述设置 UI 每个页面的布局,并给出设计稿(mockup)展示 UI 的外观;
  • 设计稿只用于示意外观,其中的布局与命名可能与最终实现不同,该文档被视为最终裁决("This doc should be considered the final say")

同一目录下还有两份配套文档,与 design.md 互为补充:

  • [spec.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/spec.md):定义设置 UI 的基本功能——如何禁用 UI、导航项、启动方式与设置编辑保存机制;
  • [cascading-settings.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/cascading-settings.md):探讨级联设置(cascading settings)与 profiles.defaults 如何在设置 UI 中表示。

值得注意的是,当时 Windows Terminal 的默认设置体验是用文本编辑器打开 settings.json,设置 UI 是全新引入的界面,因此设计文档同时给出了导航方案对比(更细粒度的分类导航 vs 与 JSON 结构对齐的导航),并在 [spec.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/spec.md) 中记录了备选方案(新窗口启动、自动保存等)及各自的取舍。

二、顶层导航设计:General / Appearance / Profiles / Keyboard

design.md 给出的左侧导航栏顶层结构如下(带星号的页面在对应功能实现后才加入):

  • General
    • Startup
    • Interaction
    • Rendering
  • Appearance
    • Global
    • Color schemes
    • Themes*
  • Profiles
    • Defaults
    • Enumerate profiles
    • Add new
  • Keyboard
  • Mouse*
  • Command Palette*
  • Marketplace*

在 [spec.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/spec.md) 中,还记录了另一套被否决的导航方案——"与 JSON 对齐":顶层只有 Globals、Profiles、Color schemes、Bindings 四项(Bindings 下挂 key bindings、mouse bindings 与 command palette)。最终产品选择了更细分、更易消化的分类导航,理由是"与其他终端更一致"。

对照源码:今天的导航项与导航标签

MainPage.xamlNavigationView.MenuItems 可以看到,现行导航项已演进为平铺的页面列表:Launch(对应原 General-Startup)、Interaction、Appearance、Color Schemes、Rendering、Compatibility、Actions(带 New 徽标)、New Tab Menu、Extensions,加上 Profiles 分组头与 Base Layer(x:Uid="Nav_ProfileDefaults",即级联设置文档中把 "Global" 页改名后的 "Base layer")。导航标签常量集中定义在 NavConstants.h,其中 til::static_map 把每个 NavigationTag 映射到 Segoe MDL2 Assets 图标字形,例如 launchTag → \xE7B5actionsTag → \xE765(Keyboard Classic)。

spec.md 中"导航菜单底部放一个 'Open the JSON file' 按钮"的设计也已落地:MainPage.xamlFooterMenuItems 中有 OpenJsonNavItemTag="OpenJson_Nav"),用户随时可以从 UI 回到原始 JSON 文件编辑。

三、Appearance 页设计:内嵌 TerminalControl 的实时预览窗口

design.md 指出 Profile appearance 页需要特殊设计,因为它内嵌了一个 TerminalControl 预览窗口,用于实时预览外观变更。该预览窗口出现在两个页面:

  • Appearance - Color Schemes
  • Profiles - Appearance

![Appearance 页内嵌预览窗口的设计稿](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/appearance.png)

源码印证:预览窗口不启动真实进程

PreviewConnection.cpp 实现了这个预览窗口背后的数据源:它实现了一个 TerminalConnection,但 WriteInput/Resize/Close 全部为空操作,Start() 只通过 TerminalOutput.raise(...) 向渲染层写出一段固定的示例文本——包括硬重置序列(RIS)、Windows Terminal 标题、一段仿 git diff -w 的彩色输出(红 - Windows Console、绿 + Windows Terminal!、灰 Write-Host "🛃!")。它还根据字体是否支持 powerline 字形在 PromptTextPlainC:\> )与 PromptTextPowerline\x1b[49;34m\xe0b6… 等 ANSI 序列拼出的 C:\ 提示符)之间切换并重新发送。

从源码结构看,预览窗口与真实终端共用同一个 TerminalControl 渲染管线,只是数据源换成 PreviewConnection:用户每改一项颜色、字体或 acrylic 参数,渲染层就会用新的设置重绘这段固定样例,从而在点击保存前看到外观效果——这正是 spec.md 中"在写回 settings.json 之前预览变更"这一决策的实现路径。

四、Keyboard 页设计:绑定列表与动态生长的编辑弹窗

design.md 对键盘页的描述包含三个要点:

  1. 页面列出所有已启用的按键绑定,并提供添加与删除入口;
  2. 悬停某一项时出现 Edit 与 Delete 按钮;
  3. 点击 Edit 弹出模态框:对于无参数/无 actions 的命令,模态框只显示命令与按键输入区;若命令带有额外参数或 actions,模态框会随参数/actions 的添加动态增高。设计稿同时注明,希望未来的输入框能"监听"按键组合,即界面上增加一个 "listen" 按钮。

![键盘绑定编辑弹窗(无参数命令)设计稿](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/keyboard-modal.png)

今天该页面已演进为 Actions 页Actions.xamlEditAction 承担"绑定列表 + 编辑弹窗"的职责,而 "listen" 构想则由 KeyChordListener 组件落地——它暴露一个 Keys 依赖属性(DEPENDENCY_PROPERTY(Control::KeyChord, Keys))并在 KeyChordTextBox_KeyDown 中捕获按键事件把组合键写入 KeyChord,即"点击输入框后直接按键即可录入组合"。

五、完整设置清单:八个页面的控件布局

design.md 的核心内容是一张覆盖全部设置项的布局表。标题行与 UI 左侧的导航视图对齐,列中加粗的标题(如 GeneralAppearanceAdvanced)对应页面顶部的 pivot 导航。以下完整继承原文档表格:

General - Startup General - Interaction General - Rendering Appearance - Global Appearance - Color Schemes Profiles - Global Profiles - Enumerate profiles Profiles - Add new
Default profile (dropdown) Copy after selection is made (checkbox) Software rendering (checkbox) Theme (radio) Name (text box) General General General
Launch on startup (checkbox) Copy formatting (checkbox) Screen redrawing (checkbox) Show/Hide the title bar (checkbox) Cursor color (color picker) Command line (text box) Scrollbar visibility (radio) Scrollbar visibility (radio)
Launch size (radio) Word delimiters (text box) Show terminal title in title bar (checkbox) Selection background (color picker) Starting directory (browse button) Command line (browse button) Command line (browse button)
Launch position (text box) Window resize behavior (checkbox) Always show tabs (checkbox) Background (color picker) Icon (browse button) Starting directory (browse button) Starting directory (browse button)
Columns on first launch (number picker) Tab width mode (radio) Foreground (color picker) Tab title (text box) Name (text box) Name (text box)
Rows on first launch (number picker) Hide close all tabs popup (checkbox) Black (color picker) Scrollbar visibility (radio) Icon (browse button) Icon (browse button)
Automatically create new profiles when new shells are installed (checkbox) Blue (color picker) Appearance Tab title (text box) Tab title (text box)
Cyan (color picker) Font face (text box) Appearance Appearance
Green (color picker) Font size (number picker) Retro terminal effects (checkbox) Retro terminal effects (checkbox)
Purple (color picker) Font weight (dropdown) Font face (text box) Font face (text box)
Red (color picker) Padding (text box) Font size (number picker) Font size (number picker)
White (color picker) Cursor shape (radio) Font weight (dropdown) Font weight (dropdown)
Yellow (color picker) Cursor color (color picker) Padding (text box) Padding (text box)
Bright black (color picker) Cursor height (number picker) Cursor shape (radio) Cursor shape (radio)
Bright blue (color picker) Color scheme (dropdown) Cursor color (color picker) Cursor color (color picker)
Bright cyan (color picker) Foreground color (color picker) Cursor height (number picker) Cursor height (number picker)
Bright green (color picker) Background color (color picker) Color scheme (dropdown) Color scheme (dropdown)
Bright purple (color picker) Selection background color (color picker) Foreground color (color picker) Foreground color (color picker)
Bright red (color picker) Enable acrylic (checkbox) Background color (color picker) Background color (color picker)
Bright white (color picker) Acrylic opacity (number picker) Selection background color (color picker) Selection background color (color picker)
Bright yellow (color picker) Background image (browse button) Enable acrylic (checkbox) Enable acrylic (checkbox)
Background image stretch mode (radio) Acrylic opacity (number picker) Acrylic opacity (number picker)
Background image alignment (dropdown) Background image (browse button) Background image (browse button)
Background image opacity (number picker) Background image stretch mode (radio) Background image stretch mode (radio)
Retro terminal effects (checkbox) Background image alignment (dropdown) Background image alignment (dropdown)
Advanced Background image opacity (number picker) Background image opacity (number picker)
Hide profile from dropdown (checkbox) Advanced Advanced
Suppress title changes (checkbox) GUID (text box) GUID (text box)
Antialiasing text (radio) Hide profile from dropdown (checkbox) Hide profile from dropdown (checkbox)
AltGr aliasing (checkbox) Suppress title changes (checkbox) Suppress title changes (checkbox)
Scroll to input when typing (checkbox) Antialiasing text (radio) Antialiasing text (radio)
History size (number picker) AltGr aliasing (checkbox) AltGr aliasing (checkbox)
How the profile closes (radio) Scroll to input when typing (checkbox) Scroll to input when typing (checkbox)
History size (number picker) History size (number picker)
How the profile closes (radio) How the profile closes (radio)

表格与真实默认值 JSON 的对应关系

表中每一项都对应 defaults.json 中的一个 JSON 键。该文件是系统级默认值层(设计文档中"system set value"的来源),节选可验证上述表格的取值形态:

  • Startup 组:initialCols: 120initialRows: 30launchMode: "default" 对应 "Columns/Rows on first launch" 与 "Launch size" 控件;
  • Interaction 组:copyOnSelect: falsecopyFormatting: truewordDelimiters: " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502" 对应 "Copy after selection is made / Copy formatting / Word delimiters" 三个控件;
  • Appearance - Global:alwaysShowTabs: trueshowTerminalTitleInTitlebar: truetabWidthMode: "equal"theme: "dark" 对应表中 "Always show tabs / Show terminal title in title bar / Tab width mode / Theme" 控件;
  • Profiles 层:historySize: 9001closeOnExit: "automatic"antialiasingMode: "grayscale"altGrAliasing: truesnapOnInput: true 等对应 Advanced pivot 下的 History size、How the profile closes、Antialiasing text、AltGr aliasing、Scroll to input when typing 控件;useAcrylic: false 对应 Enable acrylic。

这验证了 [cascading-settings.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/cascading-settings.md) 中所述的三级取值链:用户 settings.json 值 → defaults.json 值 → 系统内建值

六、保存机制:Save 按钮、未保存提示与 Portable 模式

spec.md 对编辑保存的决策是"实现一个 Save 按钮":

  • 用户只有点击 Save 后,变更才写回 settings.json——与今天用文本编辑器改 JSON 并保存的行为对齐;
  • 被否掉的备选方案是"边改边自动保存",实时生效但失去了与 JSON 工作流的对等性。

当前实现与该决策一致,且功能更多:MainPage.xaml 底部条包含 SaveButton(AccentButtonStyle 强调按钮)与 ResetButton,左侧有一个默认 CollapsedSettings_UnsavedSettingsWarning 文本(Goldenrod 颜色)用于提示存在未保存变更;当 CascadiaSettings.IsPortableMode 为真时还会显示 Portable 模式说明与链接。保存/重置的实际处理逻辑在 MainPage.cppSaveButton_Click / ResetButton_Click 中。

spec.md 同时要求整个 UI 通过完整的无障碍测试:所有条目必须可被屏幕阅读器和键盘访问,并全量本地化。从 TerminalSettingsEditor/Resources 下覆盖 de-DE、zh-CN、ja-JP 等十余种语言的 Resources.resw 目录结构看,本地化要求已被落实。

七、级联设置在 UI 中的表示:Base layer、重置与跨 Profile 复制

[cascading-settings.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/cascading-settings.md)(Carlos Zamora 与 Kayla Cinnamon,2020-11-10)探讨的是:既然每个设置的值可能来自 profile 自身、profiles.defaults 或 defaults.json 多层声明,设置 UI 应如何表达"覆盖/继承"关系。文档给出了一组将组合使用的 UI 提案:

  1. 控件下方文字标注:把 Profiles 下的 "Global" 页改名为 "Base layer";覆盖了 base layer 的设置,控件下方显示 "Overrides Base layer." 文字,标题旁提供一个带 "Reset" 提示的重置按钮;
  2. Add New → Duplicate Profile:新增 Profile 入口改为一个选择页,用户可以在"复制某个已有 profile"与"从默认设置新建"之间单选,随后进入的 Profile 页会按所选来源预填字段;
  3. Reset Profile 按钮:在 Profile 页的 Advanced pivot 底部提供 "Reset to default settings",清除该 profile 对象内的自定义设置,优先级回落到 profile.defaults 然后 defaults.json;
  4. "Apply to all profiles":每个 Profile 的 Advanced 页提供 "Copy settings to..." 按钮,弹出对话框以树视图列出全部 profile 设置项供勾选,底部以复选框列出所有 profile 作为复制目标。

文档还为每个提案对比了同类终端的做法(ConEmu/Cmder 的 Clone profile、iTerm2 的 Bulk Copy from Selected Profile 等),并记录了两个被否决的方案及其理由:

  • <inherit> 下拉项:每个设置变成可编辑 ComboBox、附带 inherit 选项。优点是界面不杂乱,缺点是每个设置都是下拉框,且颜色选择器在该模型下无法自洽;
  • 锁按钮:锁住 = 继承自 Global 且控件禁用。缺点是语义易误解("锁住"直觉上表示"固定在本 profile",与当前设计恰好相反)。

源码印证:Has/Clear/OverrideSource 三件套

cascading-settings.md 指出 XAML 层会为每个设置引入一个 ContentControl 包装控件,并利用 TerminalSettingsModel 提供的四件套 API:

// Note: String and "Name" are replaced for each setting
bool HasName();
void ClearName();
String Name();
void Name(String val);

这套接口在 IInheritable.h 中由宏体系完整实现:

  • INHERITABLE_SETTING 宏为每个设置生成 Has<NAME>()(判断用户是否显式设置过值)、<NAME>OverrideSource()(返回提供当前解析值的那一层对象)、Clear<NAME>()(清回继承态)与 getter/setter;
  • getter 的解析注释直接写着 fallback 顺序:user set value --> inherited value --> system set value——与文档摘要中"settings.json 值 → defaults.json 值 → 系统值"的级联链一一对应;
  • 每个设置存储为 std::optionalnullopt 表示"必须向父层继承";_parents 是一个 std::vector<com_ptr<T>>AddLeastImportantParent / AddMostImportantParent 维护父子优先级,<NAME>OverrideSource() 会沿父链回溯找到第一个显式设置该值的祖先;
  • 文档特别提到 INHERITABLE_NULLABLE_SETTING 用于 Profile.Foreground 这类"null 本身是合法值"的可空设置,用双层 optional(NullableSetting<T> = std::optional<std::optional<T>>)区分"未设置需继承"与"显式清空为 null"两种状态。

正是 OverrideSource() 让 UI 能精确回答"这个值到底来自哪一层",从而驱动 "Overrides Base layer." 标注与每行重置按钮——设计文档中抽象的 API 需求在这里有了逐行对应的实现。

八、能力与兼容性:文档中的边界声明

spec.md 的 Capabilities 一节明确了该功能的能力边界,写作与评审设置 UI 相关代码时值得参照:

  • 无障碍:全新 UI 元素,必须完成屏幕阅读器与键盘全路径测试,且全量本地化;
  • 安全 / 可靠性 / 性能功耗:均不受影响;
  • 兼容性:默认体验从"文本编辑器打开 JSON"变为"打开设置 UI",且该行为可通过修改 openSettings 键绑定加 settingsUI 选项回退——对应今天的 Actions 页中 openSettings 的 settingsUI 参数;
  • 未来考量hidden 属性需要特殊处理(理想情况下所有 profile 无论 hidden 与否都应出现在设置中)、需要撤销功能、Marketplace 实现后加入顶层导航、导航结构随功能增长持续调整。

cascading-settings.md 另指出一个兼容性取舍:设置 UI 只提供 JSON 的"部分对等"(partial parity)是有意的——它是面向普通用户的简化工具,若为追求全量对等而堆满选项,会牺牲设置 UI 的简洁性这一核心价值。

九、延伸阅读路径

围绕本文主题,仓库内可按以下路径继续深入:

  • 设计稿三件套:[design.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/design.md)、[spec.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/spec.md)、[cascading-settings.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#1564 - Settings UI/cascading-settings.md);
  • UI 实现:MainPage.xaml(导航、搜索框、Save/Reset 底栏)、NavConstants.h(导航标签与图标映射)、PreviewConnection.cpp(外观预览数据源)、KeyChordListener.h(组合键录入)、ActionEntry.cpp 所在的 Actions 模型;
  • 设置模型与继承机制:IInheritable.h(继承链与可清空设置宏)、Profile.hdefaults.json(系统默认值层,文件头注明为自动生成,修改会被忽略);
  • 更宏观的模型背景可参阅 [Terminal Settings Model 规格](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#885 - Terminal Settings Model/#885 - Terminal Settings Model.md) 与 TerminalSettings-spec.md

需要说明适用前提:design.md 成文于 2020 年,文中 "Launch on startup"、"Launch position" 等 Startup 组控件与现行 Launch.xaml 的项集合已有差异(现行版本新增了 startupActions、alwaysOnTop 等,defaults.json 中可见);但导航分层思路、Appearance 预览窗口、Save 保存模型与级联继承表达这四大设计决策,在今天的源码中仍然成立。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
981
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384