TUI——用户流程从文本循环到屏幕交互
有交互CLI的用户流程是一个文本循环:输入命令→输出文字→再输入。但当程序需要让用户「浏览列表」「选择项目」「查看详情」时,纯文本的命令循环就力不从心了。TUI在终端屏幕上划分区域,用户在区域之间移动焦点、触发操作——用户流程从「输入命令」变成了「在屏幕上导航」。
一、从命令到屏幕 有交互CLI:
1 2 3 4 5 6 7 8 9 > list 1. process A (PID 100) 2. process B (PID 200) 3. process C (PID 300) > select 2 selected: process B > details PID: 200, Memory: 45MB, Threads: 12 > exit
用户需要记住 自己看到了什么、选择了什么、下一步要做什么什么。信息是线性的,上下文靠记忆维持。
TUI:
1 2 3 4 5 6 7 8 9 10 11 ┌──────────────────────────────┐ │ Processes [v1.0]│ ├──────────────┬───────────────┤ │ PID Name │ Details │ │ 100 abc.exe │ PID: 100 │ │>200 xxx.exe │ Name: xxx.exe │ ← 焦点在这里 │ 300 yyy.exe │ Memory: 45MB │ │ │ Threads: 12 │ ├──────────────┴───────────────┤ │ ↑↓ Move Enter Detail q Quit│ └──────────────────────────────┘
用户不需要记住 ——屏幕同时展示了列表和详情。焦点用 > 标记,操作提示在底部。信息是空间的,上下文靠屏幕维持。
TUI的核心变化:信息从线性文本变成了空间布局。
二、屏幕区域:TUI的物理结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 flowchart LR subgraph "TUI 屏幕" direction TB Title["标题栏"] subgraph "中部区域" direction LR List["列表区域<br/>↑↓ 移动<br/>Enter 选择"] Detail["详情区域<br/>显示选中项信息"] end Status["状态栏 / 操作提示"] Title -.-> List Title -.-> Detail List -.-> Status Detail -.-> Status end style Title fill:#4a9eff,color:#fff style List fill:#ff9f4a,color:#fff style Detail fill:#4caf50,color:#fff style Status fill:#888,color:#fff
TUI的屏幕被划分成多个区域:
1 2 3 4 5 6 7 8 ┌──────────────────────────────┐ │ 标题栏 │ ← 区域 1:标题 ├──────────────┬───────────────┤ │ 列表区域 │ 详情区域 │ ← 区域 2 + 区域 3 │ │ │ ├──────────────┴───────────────┤ │ 状态栏 / 操作提示 │ ← 区域 4:状态栏 └──────────────────────────────┘
每个区域有自己的内容和行为:
1 2 3 4 标题栏:显示程序名称、版本 列表区域:显示可选择的项目,支持上下滚动 详情区域:显示当前选中项目的详细信息 状态栏:显示当前操作提示或状态信息
区域之间的关系:
1 2 3 4 5 6 7 列表区域 和 详情区域 是联动的: 用户在列表区域移动焦点 → 详情区域自动更新为选中项目的详情 这是一种「主从关系」: 列表 = 主区域(用户操作的目标) 详情 = 从区域(根据主区域的状态更新)
三、焦点:用户在屏幕上的「位置」 有交互CLI中,用户的「位置」是隐式的——光标在哪,用户就在哪。TUI中,焦点 是显式的——有一个明确的标记告诉用户「你当前在操作哪个区域」。
1 2 3 4 5 6 7 8 焦点在列表区域: ↑↓ → 在列表中移动 Enter → 查看详情 q → 退出 焦点在详情区域: ↑↓ → 滚动详情内容 Tab → 回到列表区域
焦点决定了用户输入被谁接收。 同一个按键(如 ↑)在不同焦点下有不同含义。
1 2 3 焦点在列表:↑ = 上移一个项目 焦点在详情:↑ = 上滚一行 焦点在搜索框:↑ = 可能无效
焦点是TUI控制流的核心——它决定了用户输入流向哪个区域。
四、焦点转移:导航的核心机制 1 2 3 4 5 6 7 8 9 10 11 graph TD A["搜索框"] -->|"Esc"| B["列表区域"] B -->|"Tab"| C["详情区域"] C -->|"Shift+Tab"| B A2["/ 键"] -->|"触发"| A B -->|"q"| D["退出"] style A fill:#ff9f4a,color:#fff style B fill:#4a9eff,color:#fff style C fill:#4caf50,color:#fff style D fill:#f44,color:#fff
用户在TUI中移动,本质上是焦点在区域之间转移 :
1 2 3 4 5 6 7 8 9 10 11 12 13 初始状态:焦点在列表区域 用户按 Tab → 焦点转移到详情区域 用户按 Shift+Tab → 焦点回到列表区域 用户按 /(搜索) → 焦点转移到搜索框 用户按 Esc → 焦点回到列表区域
焦点转移图:
1 2 3 4 5 6 7 8 ┌──────────┐ │ 搜索框 │ └────┬─────┘ │ Esc Tab │ Shift+Tab ┌────┴─────┐ │ ┌──────┴───┐ │ 列表区域 │←┼→│ 详情区域 │ └──────────┘ └──────────┘
焦点转移 = TUI的导航。 用户通过焦点转移在屏幕的不同区域之间移动。
五、用户流程的完整路径 1 2 3 4 5 6 7 8 9 10 11 12 13 14 用户流程(TUI 完整路径): 1. 启动程序 2. 看到完整屏幕布局 3. 焦点在默认区域(如列表) 4. 按键操作: a. ↑↓ → 移动焦点/滚动内容 b. Enter → 进入/确认 c. Tab → 切换区域 d. q/Esc → 返回/退出 5. 看到屏幕更新 6. 重复步骤 4-5 7. 按退出键 8. 屏幕恢复,程序退出
和有交互CLI的区别:
1 2 3 4 5 6 7 有交互CLI: 输入命令 → 看到文字输出 → 输入下一个命令 (线性文本交互) TUI: 按键 → 屏幕区域更新 → 按键 → 屏幕更新 (空间布局交互)
TUI的用户流程不是「输入命令」,而是「在屏幕上导航」。
六、TUI的程序结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 flowchart TD A["初始化屏幕布局"] --> B["设置默认焦点"] B --> C["等待按键"] C --> D{"按键类型?"} D -->|"↑↓"| E["移动焦点/滚动"] D -->|"Enter"| F["确认/进入"] D -->|"Tab"| G["切换区域"] D -->|"q"| H["退出"] E --> I["重绘屏幕"] F --> I G --> I I --> C style C fill:#4a9eff,color:#fff style H fill:#f44,color:#fff
TUI的程序结构不再是简单的命令循环,而是事件循环 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ┌──────────────────────────────┐ │ 初始化屏幕布局 │ │ 设置默认焦点 │ │ │ │ ┌──────────────────────────┐ │ │ │ 等待按键 │ │ ← 事件循环起点 │ │ ↓ │ │ │ │ 根据当前焦点分发事件 │ │ │ │ ↓ │ │ │ │ 更新屏幕 │ │ │ │ ↓ │ │ │ │ 是退出? ──是──→ 恢复终端 │ │ │ │ │ 否 │ │ │ │ └──→ 回到等待按键 │ │ ← 事件循环终点 │ └──────────────────────────┘ │ └──────────────────────────────┘
代码形态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int main () { init_screen(); draw_layout(); while (1 ) { int key = getch(); if (key == 'q' ) break ; handle_key(key); draw_screen(); } restore_terminal(); return 0 ; }
事件循环是有交互CLI的命令循环的升级版。 命令循环处理的是文字行,事件循环处理的是单个按键。
七、屏幕重绘:每次按键后的更新 有交互CLI的输出是追加的——新输出出现在旧输出下面。TUI的输出是覆盖的 ——每次按键后整个屏幕(或部分屏幕)被重绘。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 按键前: ┌──────────────────────────────┐ │>100 abc.exe │ PID: 100 │ ← 焦点在第1行 │ 200 xxx.exe │ Name: xxx.exe │ │ 300 yyy.exe │ Memory: 45MB │ └──────────────────────────────┘ 用户按 ↓ 按键后: ┌──────────────────────────────┐ │ 100 abc.exe │ PID: 100 │ │>200 xxx.exe │ Name: xxx.exe │ ← 焦点移到第2行 │ 300 yyy.exe │ Memory: 45MB │ └──────────────────────────────┘
屏幕重绘的核心问题:
1 2 3 1. 全量重绘 vs 增量重绘:每次重绘整个屏幕还是只重绘变化的部分? 2. 闪烁问题:重绘太快会导致屏幕闪烁 3. 终端兼容性:不同终端的控制序列不同
TUI的屏幕重绘是它的技术核心——和GUI的渲染循环是同一种东西,只是载体不同。
八、TUI vs 有交互CLI的本质区别
维度
有交互CLI
TUI
信息展示
线性文本
空间布局
用户输入
输入完整命令
按单个按键
上下文维持
靠用户记忆
靠屏幕显示
导航方式
输入命令
移动焦点
程序结构
命令循环
事件循环
输出方式
追加文本
覆盖重绘
交互粒度
行级(一次一行)
字符级(一次一个按键)
TUI的核心变化:交互粒度从「行」变成了「字符」。 有交互CLI一次处理一行输入,TUI一次处理一个按键。这个粒度的变化让交互变得即时——用户按下一个键就能看到屏幕变化,不需要按回车。
九、TUI的用户流程总结 1 2 3 4 5 6 7 8 9 10 11 12 13 TUI的用户流程: 启动 → 绘制屏幕 → [按键 → 焦点转移 → 屏幕更新] × N → 退出键 → 恢复终端 特征: ┌─────────────────────────────────────────┐ │ 空间布局 信息同时展示在屏幕上 │ │ 焦点机制 用户输入被当前焦点区域接收 │ │ 事件循环 按键驱动,即时响应 │ │ 屏幕重绘 每次操作后更新显示 │ │ 即时反馈 不需要按回车就有响应 │ │ 模态交互 可以弹出对话框/搜索框 │ └─────────────────────────────────────────┘
TUI相比有交互CLI的核心变化:
维度
有交互CLI
TUI
交互模型
命令循环
事件循环
信息展示
线性文本
空间布局
反馈速度
行级(需回车)
字符级(即时)
用户负担
记住上下文
屏幕显示上下文
设计重点
命令接口
屏幕布局 + 焦点管理
TUI是在终端限制下能做到的最高级交互。 但它仍然受限于字符网格——不能显示图片、不能自由拖拽、不能有任意形状的窗口。当程序需要这些能力时,就必须离开终端,进入GUI。下一篇看GUI。