同一句提交说明,我在 Tuff(一个开源的桌面指令中心)的记录里数到了三条:fix(corebox): restore input focus after show。corebox 指的是 CoreBox,按 ⌘E 叫出来的那个框。

三条的改动我对了一下,其实一模一样。前两条的作者时间连秒数都一样,只是父提交不同(应该是同一个修复被带到了不同的分支上);第三条晚了二十几个钟头,提交说明是三条里写得最全的。意思大概是这样:框显示出来以后,渲染层在 100 到 120 毫秒的时候把输入框聚焦,可原生的 window.focus() 在 100 毫秒上下落下,又把 DOM 里的焦点挪回了 body。于是用户的第一下按键,哪儿也没去。

修法是把焦点交给 CoreBox.vue 自己管:下一次渲染之后聚焦,160 毫秒再试一次,窗口拿到焦点或者收到显示事件的时候,再认领一遍。

框明明就在屏幕上,字却打不进去。提到 CoreBox 的提交有四百多条,焦点只是其中的一条线。

一个只会长高的框

先说框本身。文档里的规矩是这样的:窗口的位置固定,只有高度会变;高度跟着结果走,夹在 64 到 600 之间;渲染层发来的布局请求,主进程把 16 毫秒内的合并成一次。输入框是空的时候,它会去要一份推荐,等的这段时间窗口先不动,最多等 400 毫秒,免得先收起来再弹开。

剪贴板那边也有规矩:只有用快捷键叫出来才会自动粘贴,文本不超过 80 个字就直接填进输入框,再长的只当一个标签,文件和图片走各自的专用模式。

这些规矩,我觉得管的基本都是“看起来对不对”:位置不跳,高度不抖,连等推荐的那 400 毫秒,都不许窗口先收后展。去年九月还有一条提交,把鼠标划到哪一行就选中哪一行的行为关掉了,换成只变背景色的 hover,也算是一件管“看起来”的事。

十月四号

十月四号,仓库里多了一份规格说明和一份任务简述,还有两条提交,标题里都有焦点诊断。

规格里要求每叫出一次框,就建一份独立的记录。窗口显示以后大概 200 毫秒开始查,之后每 100 毫秒一次,一共十次,每次分别记下原生窗口、WebContents、文档和输入框是不是真的有焦点。里面有一句我觉得挺狠的:窗口可见了、调用过 focus()、或者没核实过的成功回执,都不能算拿到了焦点。

还有两处小地方。焦点相关的 IPC 以前在没有窗口的时候也会回一个 focused: true,现在要回观测到的状态;焦点申请抛出的异常,以前是悄悄吞掉的,现在有固定的错误码,能追到。

从 200 毫秒起查,任务简述里的解释是要盖过现有的几个正常调度:原生 100,渲染 120,还有 160 毫秒。采样只看,不加焦点请求,也不改变焦点。这三个数和前面那条修复的提交说明对得上,160 大概就是那次重试。如果是的话,几天前才补进去的延时,到这里成了测量要绕开的东西。

简述里还讲得很明白:这一次不修间歇性失焦的根因,不加抢焦点,不加自动重试,也不改现有的显示、收起和焦点策略。截图只算背景证据,不算复现记录,没有重现就不能说根因修好了。

整个顺序是:先写下什么才算“拿到焦点”,再去量,没有哪一步是直接动手改。

后来

后来有一条提交,标题说的是第一次叫出框的时候,一个被藏起来的覆盖层把焦点拿走了。提交说明里写,输入框在 DOM 里一直是活动元素,可文档并没有焦点,那次召唤的每个采样都报了出来;在一个隔离的开发实例里试,不加这个修复,十次采样全部失败,加上以后全部通过。

那张截图在这条提交说明里没有出现,是不是同一回事,记录里没写。