中州韵98五笔助手开发手记(1):结构设计
进度:功能设置页已完结
构思:面向对象
程序开发是一个渐进的过程,所有内容都并非最终产品必然采纳的方式
首先,由于功能需求是特例化的,所以我们造了一个处理 Yaml 文档的轮子。所以,它必然是与前端是解耦合的。在整体上,就分成了 UI前端
,与 数据后端
。
在开发过程中,前后端是即时通讯
的,在发布产品时,将采用懒加载
的方式:数据后端
,仅在接到需要修改的触发指令后才会更新一次数据,以减少不必要的内存读写。
信使:逻辑比特
前端
的控制分类几大类,比如开关类
,它就是 0
或 1
,又或者是 true
与 false
。另外还有些是程序将会内置的可选择功能,比如候选标签的样式等等,这些在 yaml
文件中,是一些字符串式的参数,但由于它是固化在程序内的,可以编制成简单的 int Item
形参传递。最后是一些可能来自用户的字符串参数,预期插入到 yaml
文件的指定位置(当然用户不必关心插入的过程是怎样的)。
梳理这些需求,就会发现前后端通信
其实是分几种情况的,但我们首先要解决的,是最基本的信使
问题。在这里,采用了 Bit Operation
的办法。我们给前后端各自设置一个同类型的 unsigned long int
作为信使
。它在 Qt
框架里是 8字节
,64比特
,这个数量足以覆盖我们对 schema
的参数管理诉求了(其实 Schema 的参数项,也没 64
个那么多)。
前端每个控件,都有分配一个开关特定比特位的权限。这样,每次通讯的开销就非常少了。当前端的某个控件被改变时,它就点亮
自己专属点位的信号灯
。从而单次传参
就能携带64
个控件点位的变更信息,为懒加载
模式提供了有力支持。
针对开关控制,逻辑比特指示开或关
针对其它控件,逻辑比特指示是否有更改
举例快捷键列表
中特定行位如果有数据变更,那这次变更数据后,相应点位就变成1
,表示灯已亮
,有变动。于是后端动同步前端时,仅需下标访问,直接索取新参数,减少了遍历与对比判断。
类设计
YAML
处理类,作如下封装:
所有盛放数据的容器,统一采用顺序容器
,在管理代码上比较方便。
首先是 IO
成员函数,采用 const bool & 1
或 const bool & 0
的形参,来管理「读入」与「写出」。是吧,1
和 I
,0
与 O
,长得还挺相像的。
在读时,要保留行数据中的空白符。YAML
的结构很好,参数多在尾部。根据 schema
的功能块,分别设置独立的容器,有些功能上固化的块儿基本是不变更的,变更的部分主要包括 switches
、speller
、translator
等,这些在直观上,似乎设为 QMap
更好,但是实际上参数变更的靶点一来在尾部,二来参数都是被读入到内存中,仅向用户展示功能名称的,使用关联容器
后,并不能发挥其特长,因为在读上,它没有下标访问快,在写上,还要为之编写独立的 print
函数。
其中,key_binder
稍显特别,因为这块儿可能涉及频繁的改动,不过我们观察到,它的结构是非常有特点的:
1 | key_binder: |
我们将之抽象为:{"toggle: zh_trad"_"Control+Shift+dollar"_always}
,即 {发送的命令,使用的热键,操控的范围}
,YAML类
并不储存该类,这些内容作为结构化的数据储存在前端的 Model
中,在需要写出时,它的 IO
成员函数在恰当的时机将之逐行取出,并作格式还原。
于是我们就获得了一个性能优异、逻辑结构高效的抽象类
,针对 RIME Schema
的结构,后期还有更大的宽容度,比如在功能参数相同的情形下,理解哪些是翻译器,哪些是正则行。后期加入文件监听后,就可以通过绑定 RIME
的现有指令,成为统盘接管 RIME功能参数
的全能助手。
外观缺憾
暂无精力研究 Qt
的样式表,在 Linux
和 MacOS
下,都很漂亮,但是在 Windows
下,界面有点复古。