一个月前发布的Alexis Bingessner
的文章
“ Text Rendering Hate You”非常接近我。
早在2017年,我就在浏览器中开发了一个交互式文本编辑器。 我对现有的ContentEditable库不满意,我想:“嘿,重新实现文本的选择! 很难吗?” 我还年轻 天真。 我认为我可以在两周内处理好它。 实际上,解决这个问题的尝试花了我几年的时间,其中包括从早到晚为一年的有偿工作,以开发新OS的文本编辑器。
在工作中,我很幸运地向
在该领域具有丰富经验的导师学习了很多东西。 我听过很多很多恐怖的故事。 包括一名工程师,该工程师支持Windows应用程序使用文本字段的自定义实现-并希望从过时的文本输入API切换到新版本。 这是此新版本
中的文本输入
界面的
列表 :

没错,有128个用于输入文本的界面。 我几乎可以肯定,有八(8!)种不同类型的锁可以解决并发问题,尽管老实说我没有阅读它们的文档,所以请不要为此引用我。 那个工程师花了一年半的时间(全职!)修改了他的编辑器,但最终失败了,仍然使用旧的API。
打字很难。
亚历克西斯(Alexis)有时会提到文本的选择,但她的个人经历与渲染更为相关。 另一方面,作为一个人,我可以添加关于输入的几点。
垂直光标移动
我已经在
上一篇文章中对此进行了
介绍 ,但是我们可以在此快速重复。
在此示例中,如果您按向上键,则光标将移至该行的开头,在单词
hello之前。 到目前为止,一切都还算合理。 但是,如果先按上下再按,光标将首先跳到
hello前面,然后再跳
一些 。
这似乎不太合逻辑。 你问他为什么跳到右边? 好吧,通过垂直移动,每个光标都可以记住以像素为单位的
x位置,并且仅在向左或向右按下(而不是上下)时更新。 同样的行为可以防止在垂直移动短线时光标向左移动。
接近度
好的,现在我们知道选择文本时,我们有
两种状态:行内的字节偏移和上述以像素为单位的
x坐标。 问题解决了吗? 好吧,不。
考虑在很长的线上的两个光标位置:
由于
loooooooooong是一个单词,因此两个光标位置
在string中具有
完全相同的字节偏移量 。 它们之间没有换行符,因为该行被软包装了。 我们的光标需要额外的一位,告诉他们要去哪一行。 大多数系统将此位称为亲和力。 它也用在混合双向文本中,我们将在稍后讨论。
表情符号修饰符
假设我向朋友发送了一条消息。 为了表达我的感受,我想添加一个有趣的表情符号。 我在文本区域中输入一个大拇指,
a
字母
a
和一个用于皮肤色调的表情符号修饰符。 看起来像这样:
哦,我不想写一封信。 我将光标置于其后,然后单击Backspace。 会发生什么? 我看到了几个选项,具体取决于编辑器。
- 错误的#1 似乎正确。 但这是文本编辑器与传统表情符号渲染(例如Sublime Text)支持一起工作的方式。 这很不好,因为轻手指表情符号被编码为黄色手指,紧接着是肤色修饰符。 并没有像预期的那样将它们组合为一个符号。 即使我从另一个应用程序复制了轻手指,它仍然无法正确显示,如下所示。
- 错误2号是Chrome 77在地址栏中的功能。 不在网页上,仅在地址栏中。 这不是渲染问题,因为带有肤色的复制粘贴表情符号有效。 取而代之的是,Chrome会删除字母,并注意到字母后面的修饰符,也会将其删除。 哎呀
- 错误的3号与应如何合并表情符号的Unicode规范匹配。 但这对用户来说是非常难以理解的,顺便说一句,您需要移动光标,以使其不会卡在表情符号中。
所有选项都是不好的,因此您可以假设可能有第四个选项。 有! 许多编辑器(例如TextEdit)甚至不允许您将光标放在字母后面,因为肤色修饰符被认为是前一个字符的单个单元。 这在表情符号的上下文中是有意义的,甚至在这种情况下也能很好地工作,但是如果修饰符由字符串中的第一个字符指示怎么办?
现在,修饰符更改换行符。 TextEdit不允许您将光标放在第二行的开头! 我个人认为此决定“也很糟糕”。
您可能还已经注意到,大拇指已变成大拇指朝下。 我自己做了这件事,以反映我对整个局势的感受。
顺便说一句,TextEdit专门使第一行上的光标
非常有问题 。 例如,猜猜我在这里按
4
怎样?
是的 您可能还认为数字之间存在空格。 他们不在那里。
双向文字
Alexis提到混合双向文本中的拆分选择,如本示例中的TextEdit:
这确实是有道理的,因为行中的阿拉伯语是从右到左编码的,因此选择似乎被拆分了,但是字节是一个连续范围。
因此,我们获得此选择有点令人惊讶:
是的,它在视觉上是连续的,但是按字节分隔。 是的,这很糟糕。 如果您使用箭头键而不是鼠标来选择文本,则某些编辑器会执行此操作。 另一种选择是用从右到左的方向交换文本内的左/右键,这也是不好的。 这里没有好的选择。
作为奖励,请尝试了解此处发生的情况:
上帝...我不想对此发表评论。
关于输入法的事情
将击键转换为输入的软件称为输入法或输入法编辑器。 对于拉丁字母来说,这不是一个非常有趣的软件,因为每次击键都直接与一个字符的插入相关联。 但是在许多脚本中,字符不适合键盘,因此您必须具有创造力。 例如,在某些中文输入法中,用户输入声音-并获得与声音相似的字符列表:
该字段有时称为合成区域,通常显示
在带下划线的文本上方 。 有时输入法必须设置样式。 例如,Android上的日语输入法使用背景色创建一个句子共享区域:
(感谢Shae的屏幕截图!)所有这些选择和组成区域是否都与双向文本交互? 让我们不要考虑它。
输入法应该在任何地方都可以使用,
即使在终端内部也可以 :
在从列表中选择汉字之前,Vim不会采取任何措施。 您可能会想:“但是它如何在Vim命令模式下工作?” 不太好 这就是为什么在Internet上文本输入和键击是单独的事件的原因。 在控制台中,它们混合在一起,导致出现问题。
这只是许多不同的文本输入方法的一个示例。 (不要忘记无键盘输入法,例如语音和手写!)幸运的是,操作系统为您提供了所有这些方法。 但是,不幸的是,您的文本框应该使用所有这些方法使用的通用文本输入协议。 对于Windows,这些是本文开头列出的128个接口。 在其他操作系统中,接口更简单,但是仍然难以实现。
您可能还会注意到,输入法是一个单独的过程,因此输入法和应用程序都可以更改文本字段的状态。 这实际上是一个并行编辑协议。 Windows通过八种(8!)锁类型解决了该问题。 尽管跨进程边界锁定似乎令人怀疑,但大多数其他平台都尝试使用不完善的启发式方法来解决并发问题。 或者他们只是希望比赛条件不会发生。 根据我的经验,祈祷不是并行性的非常有效的原始方法。
为什么一切都这么复杂?
乔纳森·布洛(Jonathan Blow)在有关软件降级的讲座中提到了他在一周内写的
文本编辑器Ken Thompson 。 本文中的大多数代码都是随机引入的复杂性。 Windows是否真的需要128个界面和8种锁来进行文本输入? 没办法 TextEdit中的错误是复杂的编辑模型的结果吗? 是的 在现代程序中散布错误是否值得担心? 至少对我而言。
但是,肯·汤普森(Ken Thompson)的编辑也比我们期望的现代文本编辑器简单得多。 Unicode支持世界上几乎所有活着的语言(大约有7,000种),并且有更多的语言已经消失。 有不同的脚本,文本方向和输入方法,每个脚本都会对任何编辑器施加复杂的限制(在某些情况下是不可解决的)。 但是他还必须支持屏幕阅读器。
不可避免地会积累大量的复杂性,在本文中,我们仅稍作改动。 这是一个真正的编程奇迹,您只需在网页上拍一下
<textarea>
立即为世界各地的每个Internet用户提供文本输入。