Tk Source Code

View Ticket
Login
Ticket UUID: 1581955
Title: text widget: window create ... -create ... doesn't work
Type: Bug Version: trunk
Submitter: nemethi Created on: 2006-10-21 20:03:47
Subsystem: 18. [text] Assigned To: fvogel
Priority: 8 Severity: Minor
Status: Closed Last Modified: 2015-03-13 20:01:20
Resolution: Fixed Closed By: fvogel
    Closed on: 2015-03-13 20:01:20
Description:
In Tk 8.5, the text widget command

    <path> window create <index> -create <script>

doesn't behave as expected.  From the man page:

"... this script will be evaluated when the annotation
is about to be displayed on the screen. ... If the 
annotation's window should ever be deleted, script will
be evaluated again the next time the  annotation is 
displayed."

In earlier Tk versions, the window's creation was
delayed indeed as specified in the man page, i.e.,
until effectively displaying it on the screen.  This
was a very useful feature, allowing the creation of a
large number of embedded windows, of which only a few
had to really exist.  For example, whenever vertical
scrolling or anything else made it necessary to update
the view, it was possible to destroy the windows
embedded into the other lines.  A very effective way to
save resources and avoid problems related to keyboard
navigation with the Tab key (which becomes awfully slow
in the presence of a large number of windows).

In Tk 8.5, the "-create" option behaves basically like
"-window".  The [window names] subcommand returns the
names of *all* embedded windows, and it is quite easy
to see that all of these really exist.  Any attempt to
delete some of them fails, because they are immediately
recreated, contrary to what is stated in the documentation.
User Comments: fvogel added on 2015-03-13 20:00:57:
Documentation fix pushed.
Closing this bug.
Thanks for the man page updates, Csaba.

fvogel added on 2015-03-08 20:56:46:
Proposed (documentation) fix in branch bug-1581955fff

fvogel added on 2015-03-01 09:46:40:
First analysis using the script I provided below on 2012-05-18:

In 8.5, at the end of the code interpreting the "window create" command (tkTextWind.c:254+), a call is made to TkTextInvalidateLineMetrics.

TkTextInvalidateLineMetrics launches a timer that will run AsyncUpdateLineMetrics.

AsyncUpdateLineMetrics is an asynchronous recomputation of the line height potentially impacted by the newly created embedded window. The goal is to get the corect lines height so that scrolling can be smooth.

This recomputation of the line height happens through the chain of calls:
AsyncUpdateLineMetrics --> TkTextUpdateLineMetrics --> TkTextUpdateOneLine --> CalculateDisplayLineHeight --> LayoutDLine

In CalculateDisplayLineHeight, one can read the following interesting comment:
 * Side effects:
 *	The combination of 'LayoutDLine' and 'FreeDLines' seems like a rather
 *	time-consuming way of gathering the information we need, so this would
 *	be a good place to look to speed up the calculations. In particular
 *	these calls will map and unmap embedded windows respectively, which I
 *	would hope isn't exactly necessary!


This is exactly what happens.


Commenting out the call to TkTextInvalidateLineMetrics in tkTextWind.c:316 makes my script work as expected (at the cost of loosing smooth scrolling).


However Csaba's script provided 2015-01-05 below still does not. There must be a further part of the story, certainly linked to the fact the code is pasted to the console at once, leading to more asynchronous calls to 'LayoutDLine' and 'FreeDLines'.
The fact these calls are asynchronous makes the mess: the user does no longer control when the embedded windows are mapped/unmapped.

We certainly have the same issue for embedded images, BTW.


In ontrast, in 8.4 none of this async stuff is happening: there is no smooth scrolling.


So dkf was right in his comment below: this bug will be really hard to fix without regressing with respect to smooth scrolling.


If fixing the documentation (as suggested by Csaba below on 2015-01-05 14:54:50) would make everyone happy I would definitely go for it.

Opinions?

nemethi (claiming to be Csaba Nemethi) added on 2015-01-05 14:54:50:
For Tablelist (where the body of a tablelist is a text widget) I found the following workaround:  Immediately after invoking "window create ... -create", I apply an elided tag to the respective text position.  This prevents the embedded window from being effectively created.  Then, whenever the visible portion of the tablelist's body text widget changes, I remove that tag from the embedded windows of the currently visible lines (this will trigger the respective creation scripts), apply it to the other embedded windows, and destroy the latters (again, due to that elided tag, the windows just deleted won't get recreated).

If it would really need too much work to restore the behavior known from Tk 8.4 and earlier, I think the man page should be corrected accordingly and extended by the following 3 comments:

1) Eliding an embedded window immediately after scheduling it for creation via "window create ... -create" will prevent it from being effectively created.

2) Uneliding an elided embedded window scheduled for creation via "window create ... -create" will automatically trigger the associated creation script.

3) After destroying an elided embedded window, the latter won't get automatically recreated.

nemethi (claiming to be Csaba Nemethi) added on 2015-01-05 14:40:20:
Another test script enabling one to reproduce this bug:

pack [text .t -height 5] -expand yes -fill both

proc createLabel line {
    set w .t.l$line
    puts "creating label $w"
    label $w -text "Label $line"
    return $w
}

for {set line 1} {$line <= 10} {incr line} {
    .t window create $line.0 -create [list createLabel $line]
    .t insert end \n
}

after 2000 {
    puts "\n2 seconds later:"
    for {set line 1} {$line <= 10} {incr line} {
        destroy .t.l$line
    }
}

On my Linux box, with Tk 8.5 and 8.6, I get the output:

creating label .t.l1
creating label .t.l2
creating label .t.l3
creating label .t.l4
creating label .t.l5
creating label .t.l6
creating label .t.l7
creating label .t.l8
creating label .t.l9
creating label .t.l10

2 seconds later:
creating label .t.l1
creating label .t.l2
creating label .t.l3
creating label .t.l4
creating label .t.l5
creating label .t.l6
creating label .t.l7
creating label .t.l8
creating label .t.l9
creating label .t.l10

Actually, the text widget (of height 5) can only display 4 lines (because the embedded labels make the lines a bit higher).  For this reason, it is a bug that all 10 labels get created (and recreated).

With Tk 8.4, only 4 labels are created.  This is the correct behavior:

creating label .t.l1
creating label .t.l2
creating label .t.l3
creating label .t.l4

2 seconds later:
creating label .t.l1
creating label .t.l2
creating label .t.l3
creating label .t.l4

fvogelnew1 added on 2012-05-18 03:22:41:
Test script:

pack [text .t]
for {set i 1} {$i < 100} {incr i} {
  .t insert end $i\t$i--$i--$i--$i--$i\n
}
proc cremwin {} {
  puts "NOW running the -create script"
  return [entry .t.e2]
}
# create embedded window out of the currently visible area
# In Tk 8.5 the message displays as soon as we hit enter
# on next line - BUG
# In Tk 8.4 the message displays only when scrolling down
# so that line 50 becomes visible - OK
.t window create 50.5 -create cremwin

dkf added on 2010-01-05 16:11:13:
Bluntly, there's not much chance of fixing this unless we can find someone who really groks the (now horrendously complex) text widget code in enough detail. My suspicion/hunch is that it'll be hard to fix because we now need the dimensions of the embedded windows much earlier so that we can get smooth scrolling right.

nemethi added on 2009-10-21 04:25:32:
Any chance to see this annoying bug fixed in the near future?