2

How can I configure the listbox component width in a combobox, so that it will fit to longest entry? The widget width itself (the entry component) can be short, but I'm looking for a way to configure the listbox component to be wider than the entry...

Jerry
  • 70,495
  • 13
  • 100
  • 144
HardwareEng.
  • 99
  • 1
  • 14

1 Answers1

0

This is possible, but not without a bit of hacking ;)

You can find the implementation of combobox in combobox.tcl (in my case /usr/share/tcltk/tk8.5/ttk/combobox.tcl. There you see that if you have a combobox

set cb [ttk::combobox .cb -state readonly]

and invoke it, it creates the following internal widget structure:

$cb.popdown.f.l

popdown is the toplevel that pops down if you click the combobox, f a frame, and l the actual listbox which contains your values. To fit the longest entry you need to modify the geometry of the popdown toplevel.

We try to do so by binding a resize script to theButtonPress event on the combobox, however, this does not work because by default the bindings are handled in the following order (output of puts [bindtags $cb]):

.cb TCombobox . all

So first the events on the widget (.cb) are handled, then the events on the class (TCombobox), and then on the toplevel (.) and finally the events that are bound to all widgets. What this means is that when we click the combobox, first our resize script is executed, but then the popdown toplevel does exist yet, because it will only be created when handling the class event.

The solution is to switch the event handling order for the widget and class:

bindtags $cb [list TCombobox $cb . all]

Now it should be working! Below a minimal proof of concept:

package require Tk                                                              

wm title . "Combobox Listbox resize"                                            
wm geometry . "150x40"                                                          
grid columnconfigure . 0 -weight 1                                              
set cb [ttk::combobox .cb -width 20 -state readonly]                            
grid $cb -row 1 -column 0 
set cmd "$cb configure -values {abarsdhresntdaenstdsnthret erstihre reshterhstierht}"
$cb configure -postcommand $cmd                                                 
bind $cb <ButtonPress> changeGeomPopdown
bindtags $cb [list TCombobox $cb . all]                                         

proc changeGeomPopdown { } {                                                    
    global cb                                                               
    scan [wm geometry $cb.popdown] "%dx%d+%d+%d" w h x y
    wm geometry $cb.popdown "300x${h}+${x}+${y}"                            
}

To get the desired width you need to ask for the font of the listbox [1]:

$cb.popdown.f.l cget -font

Then use the font measure command ([2]) on every string to determine the pixels you need to fit all entries. Don't forget to add a bit of padding to for a better look and to make sure the scrollbar doesn't overlap with your text.

NB: To avoid all kind of nasty stuff please make sure that this internal widget structure is in place before you try to modify it, the implementation of ttk::combobox might change and then your program won't work anymore!


[1] http://www.tcl.tk/man/tcl8.5/TkCmd/listbox.htm#M16
[2] http://www.tcl.tk/man/tcl8.5/TkCmd/font.htm#M10

Gerhard Burger
  • 1,379
  • 1
  • 16
  • 25
  • Perhaps a horizontal scroll bar would be simple workaround (even though not as nice as a perfectly sized selection list). – R Yoda Jan 06 '16 at 10:38