Auto-Complete for Entry Widgets

AJB [2005/01/03] - This is a quick little proc that uses a list to perform auto-completion in an entry box as the user enters data. Should work with other types of entry widgets as well, like the Tile combobox. Although, in the case of a combobox, this functionallity should probably be built into the widget, considering that the widget already has a list of values.

And, if you dont understand the test code, then you are probably not very cultured, and you should try to watch more classic films. ;)

SRIV I'm boycotting the MPAA for at least the next 40 years

AJB SRIV: That should not be a problem, Monty Python is British made. ;)

#use autocomplete in the validate command of an entry box as follows
#     -validatecommand {autocomplete %W %d %v %S %P $list}
# where list is a tcl list of values to complete the entry with
proc autocomplete {win action validation delta value valuelist} {
    after idle [list $win configure -validate $validation]
    if {$value ne "" && ($action == 1 || ($action == 0 && [string length $delta] == 1))} {
        set pattern [string map {\\ \\\\ \[ \\\[ \] \\\] * \\* ? \\?} $value]*
        set pop [lsearch -inline $valuelist $pattern]
        if {$pop ne {}} {
            $win delete 0 end
            $win insert end $pop
            $win selection range [string length $value] end
            $win icursor [string length $value]
            return 1
        }
    }
    $win selection clear
    return 1
}


#Test code--- change to if 1
if 0 {
package require Tk
 
set fruitlist {apple banana cherry grape grapefruit lemon loganberry mango orange \
                {passion fruit} pear plum pomegranate prune raspberry}
 
entry .test -validate all -validatecommand {autocomplete %W %d %v %S %P $fruitlist}
label .top -text "Self Defense Against Fresh Fruit"
label .ret -wraplength 300
button .done -text Go -command {.ret configure -text "It's quite simple to defend yourself against \
                        a man armed with a [.test get].  First of all you force him to drop the [.test get].  \
                        Second, you eat the [.test get], thus disarming him.  You have now rendered him harmless."}
grid .top -pady 5 -columnspan 2
grid .ret -columnspan 2
grid .test .done
}

It's safest to put the strings in the list sorted by length. For example see what happens if you

 set fruitlist [concat aaaabbb $fruitlist aaa]

and then put an a in the entry widget.

RFox

MAK A regular lsort will do that implicitly. Just use [lsort $valuelist] before lsearch.

AJB The sorting is important, however, it is also important that the list be sorted elsewhere. This script is called every time the user presses a key. If you are working with a large list, it could cause additional performance issues.

Bryan Oakley for all practical purposes I don't think there can be much of a performance issue. I can sort, for instance, 100,000 integers in about 75 milliseconds. You would have to type about 13 characters per second to get ahead of that. However, sorting on every keypress seems dubious -- unless the dataset can possibly change between keystrokes you really only have to do the sort on the first keypress, or whenever the dataset changes.

Beware This code works great. However, a lot of my $valuelist has backslashes in the strings. The autocomplete seems to stop after one is typed. Any work around for that?

DKF 2017-09-23: I've updated the code above to be a version that handles both insertion and deletion (after question here ) and doesn't choke on values in the list that have glob metacharacters in (because it string maps things before searching). As it is pretty much a pure upgrade in usability over what was there before, I've changed the code rather than writing it below.