Enhancing BWidget

Richard Suchenwirth 2002-08-17 - The BWidget package is a set of powerful megawidgets written in pure Tcl. Examination of the source code shows that it is easy to add functionality which can be called in Tk's object-oriented way: if you provide a proc Class::command, where class is one of BWidget's widget classes, calls to a widget of that class with that command will be dispatched to your "method", e.g.

 proc Foo::grill {w ...} {...}
 Foo .bar
 .bar grill args ;# calls Foo::grill .bar args

I employ this simple protocol to extend ComboBoxes for two typical use cases (to appear in starDOM):

  • a "history", where previous inputs are recorded in the list of values used as a stack - the last input is on top (just below a blank field for new random input);
  • a "chooser" where you can only select one of the pre-defined alternatives (basically just a reconfiguration).


 package require BWidget
 namespace eval ComboBox {#needed to extend BWidget functionality}

 proc ComboBox::enable {w what args} {
    switch -- $what {
        history {
            $w configure -values {{}} ;# always keep a blank on top
            foreach evt {<Return> <FocusOut>} {
                $w bind $evt {+ ComboBox::_add %W [%W cget -text]}
        chooser {
            set values [$w cget -values]
            set width 0
            foreach i $values {
                set sl [string length $i]
                if {$sl > $width} {set width $sl}
            set bg [[label .dummy] cget -bg]
            destroy .dummy
            $w setvalue first
            $w configure -width [expr {$width+1}]
            $w configure -editable 0 -relief flat -entrybg $bg ;# (1)
    if {$args != ""} {eval [list $w configure] $args}
 # This "internal" function is underscore-prefixed:
 proc ComboBox::_add {w item} {
    set w [winfo parent $w] ;# binding comes from entry
    set values [$w cget -values]
    if {[lsearch -exact $values $item] < 0} {
        $w configure -values [linsert $values 1 $item]
 #------------------------------------------- Test and demo code:
 if {[file tail [info script]]==[file tail $argv0]} {
    ComboBox .c -label "Search " -textvariable query
    .c bind <Return> {puts [list search -$mode $query]}
    .c enable history
     ComboBox .m -values {case nocase regexp XPath} -textvariable mode
     .m enable chooser
     pack .c .m -side left
     bind . <Escape> {exec wish $argv0 &; exit}
     bind . ? {console show}

#(1) I first had "grey" there which looks ok on Win 95, but not on Win2k. The better way is to read the default background from another widget (".dummy" above), which is only used to cget its setting, and immediately destroyed.

TR - a method of enhancing BWidgets look and feel for different platforms is the usage of customized bindings and the option database. Making the BWidget combobox behave more Windows-like, the selected element in the dropdown listbox should follow the mouse and the active element drawn with a dotbox around it. Here's how:

 option add *Listbox.activeStyle dotbox startupFile
 bind Listbox <Motion> {
   %W selection clear 0 end
   %W selection set @%x,%y
   %W activate @%x,%y

This applies to all listboxes in the application though ...


FW: It looks like the follow feature has since been added to the package, so now you can skip all but the first line of that and just use -hottrack 1 in your combobox declaration.