Aexi(8)-Caret位置标记的再思考

来源:互联网 发布:收淘宝卖家号 编辑:程序博客网 时间:2024/06/05 20:04

新的位置标记方法

Aexi项目的开发已经闲置了很长时间了.从上次闲置下来之后到今天的这么长时间里面,我去进行了实习,并且在实习结束之后,花了几个月时间学习了计算机科学与技术专业的几门基础课程.现在,这些基础课程已经学的差不多了,于是Aexi的开发又重新开始了.
在之前的Aexi实现中,一直有一个比较大的问题就是对Caret位置的表示方法,在之前的Aexi实现中,Caret的位置表示有三种方法,第一种是Caret的绝对坐标,也就是绘制在屏幕上的时候的坐标,用x,y来标记.第二种是Caret标记的物理结构中的位置,用一个int值来表示.这个值也是文档中插入和删除文字时候的标记.第三种是文档的显示结构,用pageIndex,rowIndex,columIndex.来标记.
这三个坐标是需要严格统一的,当每次加入新的功能时,必须在外部对这个问题进行处理.比如当我们加入删除的功能时,每删除一个字符,那么就要对整个文档进行重新排版.假设删除的是第3行的第1个字符,这个字符在整个文档中占据第61个的位置,即它的documentIndex为61.那么执行删除之后,它的documentIndex变成了60,显示位置到了第2行的第20个.在删除之后,需要重新绘制Caret.而Caret的位置是根据page,row,colum这三个标记计算出来的,在删除一个图元之后,就一定要重新设置这三个标记,否则当Caret重新计算绘制位置的坐标时,它会获取第3行第1个,这时由于第三行在重新排版之后已经被销毁,就会引发空指针错误.
这给开发过程带来了不必要的复杂性,因为这三个坐标的设置原本应该是Caret内部完成的任务,现在却必须要由外部来管理.
考虑到现有的位置标记方法有这么多的问题,我设计了第二种方法来标记Caret的位置.这种位置标记的方法来自于一个简单的想法:

Caret一定是在一个Glyph的后面存在,如果Caret没有依附于一个Glyph,那么它此时肯定是在整个文档的第一个位置

事实上,只要仔细想想Caret的功能就可以理解.Caret的作用是什么?Caret的作用就是给用户即将进行的操作指定位置,有了Caret,用户就知道下一步要插入或者删除的地方在哪里了.所以文档中有Glyph,那么Caret一定会在某个Glyph的后面.如果Caret不在某个Glyph的后面,那么Caret就一定在整个文档的第一个位置.这就不难理解了.

所以我设计的新的标记方法就是去掉Caret的page,row和column的标记体系和文档中所在的位置的标记,只保留了绘制Caret所必须的绝对坐标.
然后给Caret加入了一个新的属性-宿主
Caret有一个宿主,当给Caret更换宿主的时候就把Caret的绝对位置设置到宿主的后面去.这样一来,很多操作的逻辑就变得很简单了.

  • 删除:找到Caret当前宿主的前一个宿主(如果有的话),把Caret设置过去,然后删除掉原宿主.
  • 插入:找到Caret当前宿主,获取它在整个文本中的位置,将新的Glyph插入到这个位置,将Caret的宿主更换到新插入的Glyph后面

新的标记方法带来了新的问题

这种方法看似完美无缺了,但是不得不说我的功力还是很难驾驭好相对来说比较复杂的这么一个系统.目前的实现还是有问题的.这种抽象的方法并没有建立一个完美的体系去描述Caret的位置.

那么问题出现在哪里呢?

问题就在于,当宿主Glyph是某一行的最后一个字符的时候.
Caret此时可以出现在两个位置.第一个位置就是宿主Glyph的后面.
第二个位置就是宿主Glyph的下一行的第一个位置(第一个字符的前面).
这个问题的出现很有意思,因为行的长度限制,所以一个段落被人为的分行,所以导致了在整个文本的两个Glyph的之间的一个间隙同时出现在了两个位置(某一行的最后一个字符的后面和下一行的第一个字符的前面).

这种不完美是由于人为的分行导致的,所以只能用一种不完美的办法来消解.当用户使用左右按键来调整Caret的位置的时候,这个问题是不需要解决的,当Caret被调整到当前这一行第一个字符后面的时候,下一次左移可以直接调整到上一行的最后,而不是本行第一个字符的前面.这样的做法是很好理解的,因为用户需要Caret的位置也是用来进行插入和删除操作的.所以不管Caret的显示位置在何处,只要Caret能够正确表示一个文档的操作位置即可.

当用户强行点击某一行的第一个位置时,这时虽然将Caret设置到当前行的上一行的最后所表示的文档操作位置是一样的.但是在用户看来,这是Aexi对用户的操作响应错误了.用户明明点击的是某一行的第一个位置,却跑到了上一行去.

所以这里只能用一种不那么完美的形式来解决:

  • 先将Caret的宿主设置为上一行的最后一个Glyph.
  • 在点击操作的处理方法中再额外设置Glyph的显示位置

这种办法显然还是像之前的表示方法一样,会带来不必要的复杂度.但是好在用户强制指定位置的情景只有这一种情况,后面几乎不会再有什么特殊操作会强制指定Caret的显示位置了.所以权衡之后,这样的牺牲还算可以接受.

1 0