在前面一贴关于
linum
的杂论中,我使用了双
text
的拼接来实现
linum
,关于其优点主要在于只读属性的设置。但在涉及到纵向的
scrollbar
时,我们不得不面对一个问题:如何使
linum
和文本内容跟随
scrollbar
一齐滚动?
一般而言,
scrollbar
是绑定与一个可
scroll
组件的,比如
text
或者
list
。显然,没有相应的
scrollcommand
命令的组件,例如容器组件就不属于可
scroll
组件,也就无法绑定到
scrollbar
的。因此,我们不能使用
frame
作为一个统一的外部包裹来绑定
scrollbar
。
canvas
是很好的替换选择,但有鉴于
canvas
过于强大,除非需要实现特定效果,我个人不倾向于使用这种大型组件。另外,要提一下的经验是,对于单组件绑定
scrollbar
,最好是使用
widget::scrolledwindow
,它带
optional
特征,只有在必要的时候才会显示
scrollbar
本身。
办法终归是有的,但是需要首先知道
scrollbar
到底是怎么工作的。从实际出发,我们会在两种大的情况下触发到滚动事件,第一个是直接操作
scrollbar
;第二个是操作文本内容,包括插入和光标上下移动。
对于第一种情况,比如我们点击
scrollbar
中的下标时,首先发生的并非是改变滑条的位置,相反的文本框的内容整体下移了一行,此时出发了文本框的滚屏事件,而在该事件相关的处理程序再设置
scrollbar
的滑条到相应的位置。也就是说,对于
scrollbar
的操作,最终还是要落实到
text
上来完成的。而相对与第二种情况,
scrollbar
其实只是指定了文本框的滚屏形式,例如点击下标是下滚一行,点击滑条上方的空白条是上滚一页,拖动则是滚屏一定数值。在第二种情况中,滚屏完全由
text
自身控制,比如绑定一个滚屏到文本开头的快捷键,则
scrollbar
也将相应地滑动到最上端。
text
的
command
以及
scrollbar
的
x(y)scrollcommand
命令是很需要注意的,它们其实是被其他过程用来产生命令的参数,并不直接执行。在
ttk::scrollbar
中有如下一段定义:
proc ttk::scrollbar::Scroll {w n units} {
set cmd [$w cget -command]
if {$cmd ne ""} {
uplevel
#0 $cmd scroll $n $units
}
}
显然“
$w
cget -command
”得到的比如“
.txt yview
”与之后参数结合,最后真正执行的是“
.txt yview scroll 1 unit
”这样的命令。这么做其实是限制了对
scrollbar
的操作,由于实际的使用是一个很死板的命令,除非修改
scrollbar
的定义或者使用
snit
重新构造一个,不然除了“
$w yview
”和“
$w xview
”,我们不可能得到任何有益的副作用。
text
的
command
相对好一点,它为绑定的函数添加了两个参数,分别表示可视区域的最上端和最下端处于全文的相对位置。事实上这么做是提供了编写规范,即为了设置滑块的位置,在绑定函数中一定要有
scrollbar
的
set
命令。除此之外总还是有一些自由的,双
text
滚动的
hack
就在此处进入。
当文本组件发生滚屏事件时,一方面需要设置滑条位置,另外再引发
linum
产生滚屏,如此便打到了双滚屏的效果,这里改变的无非是一个先来后到的顺序问题,要实现多组件滚动的话也是一个道理。
要设置滑条位置需要的是自动传入的两个参数,而
linum
滚屏用到的则是其中的第一个参数。命令如下:
.scrollbar set $first $last
.linum yview moveto $first
是的,这是最后的命令,只是多了一行“
moveto
”,这点事儿我却写了半天,见笑了。