Quick positioning in listbox

14 May 2003 - Eric Boudaillier

Arjen Markus asked Support for quick positioning by typing first letter in Tk features request. After playing with this feature in Windows Explorer, I have written a little command which do this to place in binding.

 # ----------------------------------------------------------------------
 #  ListboxQuickSelect --
 #
 #        This command implements quick positioning inside a listbox
 #        with key press. Each key pressed are buffered for the search.
 #
 #        Typical usage is through binding:
 #           bind $listbox <KeyPress> "ListboxQuickSelect %W %A"
 #
 # ----------------------------------------------------------------------
 proc ListboxQuickSelect {listbox key} {
    upvar 0 ::ListboxQuickSelect:$listbox data

    if {[string equal $key ""]} {
        # %A substitute non unicode character by empty string
        return
    }
    set sel [$listbox curselection]
    if {[llength $sel] > 1} {
        # Listbox has more than one selected item
        return
    }

    # Initialize index where search begin
    if {[llength $sel] == 1} {
        set startIndex [lindex $sel 0]
    } else {
        set startIndex 0
    }

    if {[info exists data(searchPrefix)]} {
        set searchPrefix $data(searchPrefix)
    } else {
        set searchPrefix ""
    }

    if {[string equal -nocase $searchPrefix $key]} {        
        # User presses twice the same key for the first char.
        # Search the next item which begin with that char (handled below).
    } else {
        append searchPrefix $key
    }

    # Skip current selected item if searchPrefix is one char
    # and selected item starts with this char.
    if {[llength $sel] == 1 &&
        [string length $searchPrefix] == 1 &&
        [string match -nocase "$searchPrefix*" \
             [$listbox get $startIndex]]} {
        incr startIndex
    }

    # Now search for searchPrefix after current item,
    # and wrap if not found.
    foreach {start end} [list $startIndex end 0 $startIndex] {
        set index $start
        foreach item [$listbox get $start $end] {
            if {[string match -nocase "$searchPrefix*" $item]} {
                set newSelectedIndex $index
                break
            }
            incr index
        }

        if {[info exists newSelectedIndex]} {
            # We have found a new item, select it and stop search
            $listbox selection clear 0 end
            $listbox selection set $newSelectedIndex
            $listbox see $newSelectedIndex
            break
        }
    }

    # Bufferize searchPrefix. Discard it in a small delay
    if {[info exists data(afterId)]} {
        after cancel $data(afterId)
    }
    set data(searchPrefix) $searchPrefix
    set data(afterId)      [after 1000 "unset ::ListboxQuickSelect:$listbox"]
 }


 # --- Test code ---

 package require Tk

 set listbox [listbox .listbox]
 eval $listbox insert end [lsort [glob -nocomplain -directory / -tail *]]
 bind $listbox <KeyPress> {
    ListboxQuickSelect %W %A
 }

 focus $listbox
 pack $listbox

ulis, 2003-05-21: Hugecombo has this functionality implemented. And has also a search-by-pattern implemented.

Hugecombo: http://perso.wanadoo.fr/maurice.ulis/tcl/Hugecombo 404 Broken Link


TS - Searching list box entries via entry box (ActiveState Cookbook) . This code allows users to search the entries in a list box while typing them in an entry box. Easily changeable to search through different indexes of the list entries.

MHo - See Listbox navigation by keyboard