I am making a simple application using Perl Tkx and allow the user to select a directory. Rather than using text wrapping or stretching the box to a ridiculous length to show the whole directory name, if a name is too long I would like to truncate it and append "..." to the end.
The problem is that the width of the label is defined to be some arbitrary value (like 40). If this value was a measurement of how many characters the label could fit I could just truncate the string to 37 and append the "..." but this doesn't seem to be the case.
Does anyone know what the -width
of a label using Perl Tkx is actually a measurement of? How can I find the amount of -width
units my string will take up so I can figure out where the appropriate point is to truncate it?
EDIT:
I found this answer in the tcl manual:
Database Class: Width Specifies a desired width for the label. If an image or bitmap is being displayed in the label then the value is in screen units (i.e. any of the forms acceptable to Tk_GetPixels); for text it is in characters.
If this option isn't specified, the label's desired width is computed from the size of the image or bitmap or text being displayed in it.
This should mean that for a width of 40 to truncate the text I should just have to truncate the string to 37 characters and add "...".
I tested this by filling the label with all "M"s. I used the letter "M" because it is typically the widest character (see here). I wrote code to truncate at 37 "M"s and add "..." to the end, but the "M"s seem to overflow past the end of the label after about 24 "M"s.
This means that it is not safe to assume that it just stretches the label to fit 40 of the widest character... thus my question is still unanswered.
How can I determine the "width" of my string so I can truncate it appropriately?
EDIT2:
I found a workaround but it is still not the solution I was looking for. If you change the text on the label to a fixed-width font it will work properly. It doesn't look as nice though so I would really like a solution that works for non-fixed-width fonts.
When the docs say that -width
for labels is interpreted as a number of characters it's probably using the average width of a character rather than the maximum width. As you discovered, when using fixed-width fonts you can work in characters and everything will come out nicely. When using variable-width fonts things get difficult quickly because there's no fixed relationship between characters and pixels.
You need to work in consistent units (i.e. pixels) and shorten the text until it fits -- preferably with a good initial guess to keep your code fast. You can get the label width in pixels via winfo reqwidth
and the text width in pixels (for a particular font) with font measure
.
use strict;
use warnings;
use Tkx;
Tkx::package_require('tile');
my $text;
my $mw = Tkx::widget->new('.');
my $label = $mw->new_ttk__label(-width => 10, -textvariable => \$text);
Tkx::pack($label, -side => 'left');
$text = limit_text($label, 'abcdefghijklmnopqrstuvwxyz');
Tkx::MainLoop;
sub limit_text {
my $label = shift;
my $text = shift;
my $font = $label->cget('-font') || 'TkDefaultFont';
my $label_width = $label->g_winfo_reqwidth;
my $text_width = Tkx::font_measure($font, $text);
my $i = int length($text) * ($label_width / $text_width);
while ($text_width > $label_width) {
$text = substr($text, 0, --$i) . '...';
$text_width = Tkx::font_measure($font, $text);
}
return $text;
}