输入法删除语义的实现:编辑中文本与已提交文本的边界
输入法删除语义的实现:编辑中文本与已提交文本的边界
输入法的工程讨论通常集中在生成侧:候选检索、词库更新、语言模型与联想。删除看起来只是生成动作的逆操作,实现上却最容易出边界问题,因为一次退格请求同时面对两份性质完全不同的文本。本文以纯血鸿蒙上的一款本地键盘为例,记录删除路径上的几个取舍,与候选质量、词库学习无关。
两份文本不能混在一个状态里
输入框中的内容可分为两类:输入法正在组合、尚未上屏的编码串(composing text),以及已提交给宿主输入框的正文(committed text)。前者由输入法维护,后者由宿主控件维护。
删除请求到达时,第一件事是判断当前是否处于组合态。若仍在组合态,退格消费编码串末尾的一个输入单元,并触发候选重算;若组合串已清空,正确做法是结束本次组合并停住,而不是继续向前删除已提交正文。把这两个状态合并处理,最典型的故障就是「多按一次退格,把上一个刚写好的字也带走了」,或者反过来,正文删了而候选栏还挂着旧编码。
组合态结束的时机同样要显式管理:字符上屏的那一刻清空组合缓冲与候选快照,避免下一次删除拿到上一轮残留的中间状态。
已提交文本的删除交给宿主,但要约束粒度
组合态之外,退格应转交宿主输入框处理,输入法不重复消费该事件。这里需要保证的是删除粒度按完整字符(而非字节或半个代理对)计算,光标随之前移;若存在选区,删除与紧随其后的写入都只作用于选区范围,选区外正文保持不变。
在句子中间执行删除时,宿主通常做局部文本更新。若输入法额外攥着一份全文镜像并据此回写,就容易出现前后拼接错位或多出空白。稳妥的边界是:输入法只维护组合串,不持有已提交正文的副本。
长按连删的节流曲线
长按退格不是简单的定时重复。可用做法是分段节流:起始若干次重复间隔较长,随后逐步缩短到一个下限,抬起时立刻清除定时器并停止重复。匀速冲到底的实现在长句回改里代价很高——用户想保留的位置往往在松手瞬间已被越过。
定时器清理要与焦点变化、键盘收起、组合态切换联动,否则会出现键盘已经收起而删除仍在继续的情况。
撤销栈归属
这一点常被忽略:宿主输入框自带的撤销应能找回误删内容,输入法不应拦截该操作,也不应把组合态的每一次编码变化写入撤销栈。否则用户按一次撤销,看到的不是刚删掉的那句话,而是半截拼音。
实现上可以让组合态的中间变化只更新内存缓冲与候选视图,不产生可撤销的编辑事务;只有上屏动作才形成一次宿主可感知的提交。这样撤销的粒度与用户理解的「写进去一句话」一致。
与外部整段写入的区别
删除路径处理的是小步、高频、可逆的编辑;从剪贴板整段写入则是一次性的批量变更,两者在焦点获取、文本变更事件的归因上走不同分支,不应共用同一套缓冲清理逻辑。把批量写入误判成组合态输入,会连带触发候选重算与不必要的重绘。
以上为实现侧的工程记录。具体策略随输入法框架、宿主控件实现与系统版本变化;离线运行场景下没有服务端可以辅助纠正状态,组合态与提交态的边界因此更值得在客户端显式建模。
更多推荐


所有评论(0)