Skip to content

DRAFT ETML: 文本块滚动原理详解

约 1004 字大约 3 分钟

elispbuffer

2025-08-17

何谓 "文本块滚动"

当我们给一段长文本限定了文本块宽度(width)之后,当文本块宽度(width)小于文本宽度(text-width)时,文本会被分割为宽度相等的多行,此时便形成了文本块。再限制一下文本块的高度,当文本块高度(height)小于文本的总行数(text-height)时,便会出现文本 height+1 行到 text-height 行之间(包含)的内容溢出文本块的情况。该如何处理溢出的文本?

有两种方式:1.直接截断,隐藏多余的文本行;2.通过滚动方式查看剩余的文本(类似浏览器网页的滚动),这种便是""文本块的滚动",接下来循序渐进介绍实现原理。

文本滚动

对给定的文本限定宽度后便形成了多行宽度相等的文本块,关于文本的渲染相关内容请看: ETML: Emacs buffer 文本块渲染

文本块渲染之后的静态文本展示在文本块内的是:从文本首行依次向下的多行文本。要想让文本动起来,就要让 "文本块内展示的文本(shown-content)"与"实际的文本(content)" 发生偏移。即当文本块的首行显示实际文本的第二行时,相对于初始的状态就实现了文本向下滚动的效果,以此类推。

我们需要一个 scroll-offset-y 变量来表示:"实际的文本"相对于"展示的文本"的偏移量,该值初始为 0,增大表示向下滚动,减小表示向上滚动。因此,我们只需要为 etml-block 模型添加该属性来渲染指定 offset 时的文本块。不断调整 offset,再重新渲染文本块便实现了简单的文本滚动效果。

实现起来也非常直观:每次取文本列表的 scroll-offset-y(+ scroll-offset-y (1- height)) 行的文本渲染到文本块中。

滚动条实现

当文本溢出文本块的高度时,需要一个滚动条表示当前文本可以滚动,接下来我们来考虑如何实现一个滚动条。

设置如下变量来渲染一个滚动条:

  • 滚动条的宽度: scroll-bar-pixel
  • 滚动条的颜色: scroll-bar-color
  • 滚动条的方向: scroll-bar-direction
  • 滚动条与边框的距离: scroll-bar-gap
  • 滚动条的样式: scroll-bar-full

除了上面的参数,渲染时还需要考虑"滚动条的高度"。根据日常经验,我们可以观察到"滚动条的高度"和"可滚动的高度"有关系:当可滚动高度越小时,滚动条高度越长;可滚动高度越大时,滚动条高度越短。

我们可以通过递推来找到这两者之间的关系:

  1. 当可滚动高度为1时,滚动条高度为 height-1,此时滚动1次滚动条到达底部。
  2. 当可滚动高度为2时,滚动条高度为 height-2,此时滚动2次滚动条到达底部。
  3. 当可滚动高度为height-1时,滚动条高度为 1,此时滚动height-1次滚动条到达底部。

由于滚动条最小高度只能为1,当可滚动高度大于 height-1 时,如何处理?

此时滚动条的一次滚动不再对应文本的一次滚动,即可能会出现文本滚动多行才对应滚动条的一次移动。接下来考虑如何计算两者的对应关系?

完整的文本块模型

缓存 + 增量更新