Ideas to Fix the Tk Scrollbar

Note: Tk 8.5 fixes basically all the problems/issues cited on this page.

Therefore everything below can be considered obsolete, but of historical interest.

GPS - I decided to create this page, so that the Tcl community can fix the Tk scrollbar. The code below demonstrates the bug, if the slider is moved.

Vince has placed a work-in-progress patch at which solves this issue, see Text widget improvements


 pack [scrollbar .s -command {.t yview}] -side right -fill y
 pack [text .t -yscrollcommand {.s set}] -side left

 for {set i 0} {$i < 300} {incr i} {
 .t insert insert $i

 for {set i 0} {$i < 20} {incr i} {
 .t insert insert "$i\n"

 for {set i 0} {$i < 300} {incr i} {
 .t insert insert $i

KRISHNA I need to have "scale-type" scrollbar instead of variable-height. So I'll have the "slider" of scrollbar be same height regardless of scrollbarred window visual size and height. Just right now it's very hard to create such kind of scrollbar. Also it'll be better to be able to change height of arrows in scrollbar.

GPS - It would seem that the scrollbar slider height is not set properly. Ideally the scrollbar should ask the widget being scrolled for the total number of lines or pixels, then that should be divided by the height of the area that the slider operates, which is subtracted from for the 3D border. That should take care of the position. The custom scrollbar code that I posted goes more in depth into this issue. I'm going to get started studying the tkScrollbar C code.

DKF - If the problem is what I think it is (that the scrollbar length changes radically as you move it) then the problem is not with the scrollbar itself, but rather with the text widget that does not seem to take into account the viewable area taken by each line when calculating the scrollbar size - it works in the logical domain only, and this only works reasonably when lines do not wrap. To make things more fun, if you turn off text wrapping, increase the number of short lines in the middle by a good few (i.e. more than the number of lines in the viewable area), and add a horizontal scrollbar as well, you should find that as you scroll vertically, the size of the horizontal scrollbar changes...

GPS - Now it makes sense that the problem is with the text widget.

GPS - I've noticed the same problem in EZWGL, Lesstif, and whatever Wily uses (9 libs?), so this seems like a more common problem than I previously thought. FW: I've seen it in AOL Instant Messenger for Windows (when mixing fonts) as well.

DML - I need to have "scale-type" scrollbar instead of variable-height. So I'll have the "slider" of scrollbar be same height regardless of scrollbarred window visual size and height. Just right now it's very hard to create such kind of scrollbar. Also it'll be better to be able to change height of arrows in scrollbar.

Vince - I've been thinking about this. The text widget currently assumes each internal line occupies just one visible line, except for those which are actually visible, for which it knows the correct value. This leads to the daft behaviour described above.

Vince, June 2003:

What are the pre-requisites for fixing this bug? Well, we need a reversible mapping from text widget contents to scrollbar position. The latter is specified by a pair of real numbers in the range 0.0, 1.0. The former must be specified at least by a line number and a character offset (or sub-line number) for the first line in the window. The only means of communication between the text widget and the scrollbar is through the passing back and forth of this pair of real numbers. So, getting to the real point, let's ignore the scrollbar (which is just a conduit for these pairs of reals), the real issue is that the text widget doesn't have a good mapping from its contents and viewable area to a pair of reals. By good we mean a mapping which:

  • returns reals which are monotonically increasing with first and last viewable index respectively
  • where the difference between the two reals returned is (approximately?) constant for a constant number of viewable lines, irrespective of either the rest of the widget's contents or the number of wrapped lines displayed.
  • is reversible (or at least temporarily reversible)

From this it seems unlikely that it is possible to fix this in a very satisfactory way without the widget knowing exactly how many visible lines it contains. This means the widget would need to cache this information (and would probably need to maintain, for each stored line a count of the number of visible lines it corresponds to). A secondary question is whether all of this actually needs to count the number of pixels rather than lines. This would work properly for the (less common, but perfectly reasonable) cases where the text widget contains different fonts, spacing, etc.

The problem with all of this, particularly in a widget containing a lot of stuff, is that the code would actually need to scan through every single line, calculating its geometry... all rather time consuming. This means we would probably want a fallback mechanism or something which only calculates the geometry in pieces in an idle callback or whatever. If the text widget is in a resizable window, then if the user manually adjusts the window width, all of these calculations would need to be repeated on the fly (all old information would no longer be valid).

This indicates that there probably needs to be a kind of proxy calculation which is more approximate (even perhaps the current method) and as time/usage allows, each line calculates its correct geometry (which is cached with some sort of geometry epoch counter). When the widget geometry changes, we still use the old information on-the-fly (so we can get good gui responsiveness), but then the old information is gradually replaced and geometry epochs updated. The somewhat tricky thing is how to do this so that the user doesn't get confused --- here the important thing is that the view must be constant, but the scrollbar may get tweaked from time to time (in an idle callback, say).

This is what I mean when I say temporarily reversible above. Clearly the mapping from text widget to scroll bar position must be reversible at any moment in time, but it is ok for the the text widget to modify that mapping over time, provided it changes both directions at once, provided the changes aren't too dramatic, and provided that the scrollbar position/size is updated at the same time. These modifications would be exactly what is happening in the idle callbacks I refer to above.

Vince, continuing...

If the above design is ok, it would at least mean adding to the TkTextLine structure a new field (integer) which is the pixel height of the line, and a line-height-calculation epoch counter, to keep track of when the calculation was last made. The Text widget as a whole would have to keep track of the total, schedule the recalculate events, and, as the calculations were updated, ensure that the appropriate commands were sent to any associated scrollbar widget. If a widget doesn't have wrapping on, then there would also be a need to keep a Text-widget wide value for the maximum line width (in pixels, again, would probably be best).

Vince, June 26th... do any other widgets have any sort of asynchronous calculation scheme that code could be shared with/borrowed from?

Theo Verelst - I have another question or remark about the slider things, when I start ActiveState wish 8.4.1 on windows XP, on a I think 1600x1200 pixel screen (and I love that), the slider on the console, and also I think on my bwise application scrollable canvas easily gets lost, i.e. unupdated and unchangeable, even though the application doesn't hang. The easiest way to check is to simply start wish, full screen the console (I didn't try on lower reso), restore it to initial size, and the slider is absent, and the screen portion seems unrefreshed.

I'm an advanced C programmer and everything, but I just happen not to be installed for an immedeate up to standard tcl/tk compile/make under cygwin, and I'm sure plowing through the source would take me some more than little time, too. I'm sure there are people out there who easily know how this can be solved?

KBK - It's a bug [L1 ], which should be fixed in 8.4.2 which is out already - FW.

DKF: Which in turn means that the bugfix is long overdue and will not be opposed by anyone.

FW: No, it's already fixed. Sorry if I caused any confusion. ;)

DKF: Oops! Was thinking about a different set of problems. ;^)