跳转到内容

区域导航

区域 给界面的一个功能范围一个稳定的名字和一条键盘路径:侧栏、结果列表、编辑窗格或详情视图。Host 用 :region 属性声明自己的区域,之后应用或用户就能跳到区域的当前位置——用户上次所在之处,或声明的默认入口——而不必 Tab 穿过中间的一切。M-g 打开选择器,它显示当前作用域里的每个区域与提示键,并读一键完成跳转;etaf-region-focus 为应用自己的按键按区域 id 执行同样的跳转。本章描述声明、入口如何解析与记忆、邻接如何决定方向键去哪里,以及选择器。Host定义 :region 属性本身;键盘定义选择器所在的键层,焦点定义它执行的焦点移动。

区域声明是一个 plist::id 是稳定身份、也是唯一必需的键,:label 是提示与帮助里用的显示名,:key 是一个提示字符,:enter 是区域回退到的 Host 引用,:neighbors 把 :left、:right、:up、:down 映射到区域 id。声明对其他一切不透明:样式、选择器与事件都不读它,没有声明的 Host 也无法被区域函数找到。

区域按 Host 祖先关系嵌套。声明在窗口区域里的窗格区域,其父级就是窗口区域;父级是最近的包含它的区域,选择器把这种嵌套显示为 Window/Editor 这样的路径。嵌套让跳转能到达面板里的窗格而不是停在面板,也是邻接被解析的层级。

声明在 Host 提交时校验:plist 结构不对、键未知、:id 缺失或不是符号、:key 不是一个字符(或是 ?)、:enter 不是 Host 引用、:neighbors 值不在四个方向内,都是 error→ etaf-renderer-error。跨 Runtime 的重复区域 id 与重复提示键在选择器打开时检测,因为身份与提示必须唯一,选择器才能无歧义。vim 方向方案下提示字符不能是 h、j、k、l,那些字母属于方向导航。

选择器列出当前焦点作用域里的区域,按文档序;直接入口搜索整个 Runtime,应用有理由时可以跳进模态作用域之外的区域。帮助列出当前作用域的区域及其标签与提示键。

区域的有效入口按固定顺序解析:该区域里最近一次被接受的焦点位置;声明的 :enter Host;区域第一个合格焦点停靠点。「合格」指已提交、已启用、已绘制(有边界)、且在区域内——区域自己的 Host 也算在自己内部。因为 Host 被移除、隐藏或禁用而不再合格的记忆位置会被跳过,列表项消失后跳转因此仍然可用。

记忆按 Runtime、按区域 id 保存。每次焦点变化都把新聚焦的 Host 记到所有包含它的区域下,因此在列表里移动会更新区域将来返回的位置,离开也不会清除它。Runtime 被处置时释放该表。

Function: etaf-region-focus (region-id &optional runtime)

Section titled “Function: etaf-region-focus (region-id &optional runtime)”

聚焦已挂载 RUNTIME 中 REGION-ID 的有效入口。

参数。 REGION-ID 是区域的 :id 符号。RUNTIME 默认为活动 Runtime。

语义。 区域在 Runtime 的全部已提交区域里查找,而不只是当前作用域,因此应用的直达键或菜单项可以跨窗格。解析入口并聚焦它遵循上面的顺序:记忆、:enter、第一个合格停靠点。选择器使用同一解析,因此提示键与程序化跳转总是落在同一个地方。这次调用不打开选择器,也不读取按键。

返回。 聚焦的 Host 引用。

错误。 没有已提交区域使用该 id 时报 error→ etaf-event-error,消息 "No ETAF region named %S";记忆、:enter 与区域内停靠点全部失败时报 "ETAF region %S has no focusable entry";Runtime 未挂载时报 error→ etaf-runtime-error。

示例。 (etaf-region-focus 'results runtime)

参见。 etaf-region-select、:region、etaf-focus-within。

区域内的方向键在区域内移动焦点;该方向在区域内找不到停靠点时,这一层就去那个方向找相邻区域。邻接先取声明的映射,其次才由区域的已提交几何决定。在与锚点同一父级的区域中,候选是那些沿请求方向完全越过锚点矩形的区域——锚点是聚焦的 Host,没有焦点时是 point 处的 Host。候选的排序是垂直重叠优先,然后轴向间隙最小,然后垂直中心距离最小,最后文档序,因此紧贴锚点旁边的区域胜过斜对角的区域,平局也有可预测的结果。声明的 :neighbors 条目优先:查找先问当前区域在请求方向上的映射,若它指到已存在的区域 id,该区域就是目标;只有没有可用声明时才由这条几何规则决定,并且每一层都先试声明、再试几何、然后才上溯父级。因此邻接默认跟随布局,声明可以覆盖它。

当前层级没有候选时,搜索上溯到父级区域,在那里找它的兄弟,因此离开窗格边缘可以到达隔壁窗口。任何地方都没有候选的方向在区域层面什么都不做:etaf-direction 随后按键盘所述落到空间焦点移动或视口滚动。

选择器是面向用户的入口,在 Runtime buffer 里绑定到 M-g,也可以从 Lisp 调用。它是叠在 echo area 上的模态单键交互,而不是一个 buffer,因此绝不打扰应用自己的窗口。

Function: etaf-region-select (&optional runtime)

Section titled “Function: etaf-region-select (&optional runtime)”

从键盘选择已挂载 RUNTIME 的一个区域。

参数。 RUNTIME 默认为活动 Runtime。

语义。 选择器按文档序列出当前焦点作用域里的区域声明,带提示键与祖先路径,并标出包含当前焦点的区域。随后它读一键。提示键聚焦该区域的有效入口,与 etaf-region-focus 完全一样。方向键按上面的几何规则移动到该方向相邻的区域,vim 方案下 h、j、k、l 同样如此。? 重复提示并给出说明。Escape 取消且不改变焦点,C-g 像任何原生 quit 一样中止,其他键什么都不改变:选择器保持打开,并在提示里显示该键的名字。跳转失败——区域没有合格入口,或该方向没有相邻区域——同样保持打开并提示,而不是结束交互或报错。框架保留 M-g 给这个选择器,因此 :keys 不能绑定它。

返回。 nil。

错误。 当前作用域没有区域时报 error→ user-error,消息 "No ETAF region is declared in this scope";两个区域共享 id 或提示键时报 error→ etaf-renderer-error;Runtime 未挂载时报 error→ etaf-runtime-error。

示例。 (etaf-region-select runtime)

参见。 etaf-region-focus、etaf-help、:region。

两个机制让选择器可靠。第一,入口记忆由 ETAF 为 :on-focus-enter 已经在跑的同一条焦点变化钩子更新,因此每次焦点移动——Tab、方向键、按压或应用调用——都会喂给区域记忆,不需要额外记账,用户回到的区域就是钩子最后记录的位置。第二,选择器在读取期间安装一个临时键覆盖,使提示字符、四个方向键、? 与 Escape 成为完整事件,而不是更长应用绑定的前缀;某个提示字符在别处也开启按键序列时,在这里仍然选择该区域。提示行在每次未知键之后重建,因此提示与可用选择始终可见。

声明问题在任何东西运行之前就被抓住:非法的 :region 值让 Host 提交失败,重复的 id 或提示键在选择器打开时报告,而不是悄悄选一个区域。入口问题由直接入口以 etaf-event-error 报告,因为应用点名了一个区域 id,理应得到具体的失败。选择器把同样的问题当作可恢复:没有合格入口的区域、没有相邻区域的方向、无法识别的键都产生提示并继续读取,用户因此可以另选区域或取消。用 Escape 取消绝不移动焦点,C-g 像任何前缀一样退出选择。