Advertisement
nurdglaw

Autocompleting combobox

May 29th, 2013
216
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TCL 12.97 KB | None | 0 0
  1. ###############################################################################
  2. #
  3. # entryWithCompletion demo script
  4. #
  5. ###############################################################################
  6.  
  7. set HISTORY {
  8.     1.0a2 nurdglaw 26-May-2013 "Standalone version. Add version number."
  9.     1.0a3 nurdglaw 27-May-2013 "Add utility procs."
  10. }
  11.  
  12. ###############################################################################
  13. #
  14. # Homebrew combobox class
  15. #
  16. #     Move this class into utils.tcl when complete.
  17. #
  18. ###############################################################################
  19.  
  20. if {[llength [info commands entryWithCompletion]] == 1} {
  21.     entryWithCompletion destroy
  22. }
  23.  
  24. oo::class create entryWithCompletion {
  25.     variable m_ent m_listboxHeight m_valuesActive m_valuesAll
  26.     variable -append m_popdownFrame m_popdownListbox m_popdownScrollbar
  27.     variable -append m_popdownToplevel
  28.  
  29.     constructor {ent values} {
  30.     set reqClass TEntry
  31.     set actClass [winfo class $ent]
  32.     if {![string equal $actClass $reqClass]} {
  33.         error "$ent has class $actClass; must be $reqClass"
  34.     }
  35.     set m_listboxHeight 5
  36.  
  37.     set m_ent $ent
  38.     set m_valuesAll $values
  39.  
  40.     my PopdownInitialise
  41.  
  42.     # Connect us to the entry widget
  43.  
  44.     $ent configure -validate key                                      \
  45.         -validatecommand [list [self object] validateCommand %P]
  46.  
  47.     bind $ent <Destroy> [list [self object] destroy]
  48.     bind $ent <FocusIn> [list [self object] focusIn]
  49.     bind $ent <FocusOut> [list [self object] focusOut]
  50.     bind $ent <Key-Down> [list [self object] keyDown]
  51.     bind $ent <Key-Up> [list [self object] keyUp]
  52.     }
  53.  
  54.     destructor {
  55.     destroy $m_popdownListbox
  56.     destroy $m_popdownScrollbar
  57.     destroy $m_popdownFrame
  58.     destroy $m_popdownToplevel
  59.     }
  60.  
  61.     method focusIn {} {
  62.     my ConfigureListbox ""
  63.     my Post
  64.     }
  65.  
  66.     method focusOut {} {
  67.     my SetEntryFromListbox
  68.     my Unpost
  69.     }
  70.  
  71.     #----------------------------------------------------------------------
  72.     #
  73.     # keyDown, keyUp
  74.     #
  75.     #     Entry widget bindings for, repectively <Key-Down> and <Key-Up>.
  76.     # Change the listbox selection repectively to the next, previous entry.
  77.     #
  78.     #----------------------------------------------------------------------
  79.  
  80.     method keyDown {} {
  81.     my ListboxSelect next
  82.     }
  83.  
  84.     method keyUp {} {
  85.     my ListboxSelect prev
  86.     }
  87.  
  88.     method validateCommand {str} {
  89.     my ConfigureListbox $str
  90.     return 1
  91.     }
  92.  
  93.     #----------------------------------------------------------------------
  94.     #
  95.     # ConfigureListbox
  96.     #
  97.     #     Get the current contents of the entry widget and set the values
  98.     # of the listbox with the object values that match it. If there are
  99.     # many values, then map the scrollbar
  100.     #
  101.     #----------------------------------------------------------------------
  102.  
  103.     method ConfigureListbox {str} {
  104.     set m_valuesActive [list]
  105.     foreach val $m_valuesAll {
  106.         if {[string match -nocase $str* $val]} {
  107.         lappend m_valuesActive $val
  108.         }
  109.     }
  110.     $m_popdownListbox configure                                      \
  111.         -listvariable [self namespace]::m_valuesActive
  112.     $m_popdownListbox selection set 0 0
  113.     if {[llength $m_valuesActive] > $m_listboxHeight} {
  114.         grid $m_popdownScrollbar -row 0 -column 1 -sticky nsew
  115.     } else {
  116.         grid remove $m_popdownScrollbar
  117.     }
  118.                                         # ttk::combobox also modifies the
  119.                                         # gridding of the listbox with -padx
  120.                                         # {1 0} when adding and 1 when removing
  121.                                         # the scrollbar.
  122.     }
  123.  
  124.     #----------------------------------------------------------------------
  125.     #
  126.     # ListboxSelect
  127.     #
  128.     #     Change the selection in the liustbox to the next or previous
  129.     # entry, according as dirn is next or prev. If that would move off the
  130.     # bottom or the top, wrap around.
  131.     #
  132.     #----------------------------------------------------------------------
  133.  
  134.     method ListboxSelect {dirn} {
  135.  puts stdout [info level 0]
  136.     set vals [set [$m_popdownListbox cget -listvariable]]
  137.     set nEnts [llength $vals]
  138.     for {set i 0} {$i < $nEnts} {incr i} {
  139.         if {[$m_popdownListbox selection includes $i]} {
  140.         set current $i
  141.         break
  142.         }
  143.     }
  144.    
  145.     switch $dirn {
  146.         next {
  147.         set new [expr $current + 1]
  148.         if {$new >= $nEnts} {
  149.             incr new -$nEnts
  150.         }
  151.         }
  152.  
  153.         prev {
  154.         set new [expr $current - 1]
  155.         if {$new < 0} {
  156.             incr new $nEnts
  157.         }
  158.         }
  159.  
  160.         default {
  161.         error "Unexpected dirn $dir"
  162.         }
  163.     }
  164.  
  165.     $m_popdownListbox selection clear 0 [expr $nEnts - 1]
  166.     $m_popdownListbox selection set $new $new
  167.     $m_popdownListbox see $new
  168.  
  169. #   $m_ent delete 0 end
  170. #   $m_ent insert 0 [lindex $vals $new]
  171.     }
  172.  
  173.     #----------------------------------------------------------------------
  174.     #
  175.     # PopdownInitialise
  176.     #
  177.     #     Configure the popdown widgets. These comprise a frame with a
  178.     # listbox and scrollbar gridded into it. (The scrollbar is not gridded
  179.     # if the listbox doesn't need one to display all its values.) The
  180.     # frame is the child of a toplevel window on whose behalf it acts as a
  181.     # transient window.
  182.     #     Configure the popdown toplevel widget, comprising a listbox and
  183.     # scrollbar gridded into a frame.
  184.     #
  185.     #----------------------------------------------------------------------
  186.  
  187.     method PopdownInitialise {} {
  188.     # Create a toplevel for the listbox
  189.     #
  190.     # We seem to need a frame within the toplevel which we can say is
  191.     # a transient for the toplevel. We need to display the listbox as
  192.     # a transient to prevent the window manage providing decoration
  193.     # (borders and a title bar).
  194.     # Note that we grid the listbox and scrollbar directly into the
  195.     # toplevel, while ttl::combobox has a frame between them.
  196.  
  197. #   set m_popdownToplevel [toplevel $m_ent.tl -class ComboboxPopdown]
  198.     set m_popdownToplevel [toplevel $m_ent.tl]
  199.     wm withdraw $m_popdownToplevel
  200.     switch -- [tk windowingsystem] {
  201.         x11 {
  202.         $m_popdownToplevel configure -relief flat -borderwidth 0
  203.         wm attributes $m_popdownToplevel -type combo
  204.         wm overrideredirect $m_popdownToplevel true
  205.         }
  206.  
  207.         win32 {
  208.         $m_popdownToplevel configure -relief flat -borderwidth 0
  209.         wm attributes $m_popdownToplevel -topmost 1
  210.         wm overrideredirect $m_popdownToplevel true
  211.         }
  212.  
  213.         default {
  214.         error "Unsupported windoing system [tk windowingsystem]"
  215.         }
  216.     }
  217.  
  218.     # Create frame and pack it into the toplevel
  219.  
  220.     set m_popdownFrame [ttk::frame $m_popdownToplevel.frm             \
  221.                 -style ComboboxPopdownFrame]
  222.     pack $m_popdownFrame -fill both -expand yes
  223.  
  224.     # Create a listbox and scrollbar grid them into the frame and link them
  225.     # together
  226.  
  227.     set m_popdownListbox [listbox $m_popdownFrame.lb                      \
  228.                   -selectmode browse                          \
  229.                   -activestyle none                           \
  230.                   -exportselection false]
  231.                                         # Flags cribbed from ttk/combox/tcl
  232.     grid $m_popdownListbox -row 0 -column 0 -sticky nsew
  233.                                         # ttk::combobox also quotes           \
  234.                                         #      -padx {1 0} -pady 1
  235.  
  236.     set m_popdownScrollbar                                            \
  237.         [scrollbar $m_popdownFrame.sb -orient vertical                \
  238.          -command [list $m_popdownListbox yview]]
  239.     grid $m_popdownScrollbar -row 0 -column 1 -sticky ns
  240.  
  241.     $m_popdownListbox configure                                       \
  242.         -yscrollcommand [list $m_popdownScrollbar set]
  243.  
  244.     # Configure the grid
  245.  
  246.     grid rowconfigure $m_popdownToplevel 0 -weight 1
  247.     grid columnconfigure $m_popdownToplevel 0 -weight 1
  248.     grid columnconfigure $m_popdownToplevel 1 -weight 0
  249.     }
  250.  
  251.     #----------------------------------------------------------------------
  252.     #
  253.     # Post
  254.     #
  255.     #     Position and display the lisbox associated with the entry
  256.     #
  257.     #----------------------------------------------------------------------
  258.  
  259.     method Post {} {
  260.     update idletasks;          # Apparently require for geometry
  261.     # propogation - presumably to proogate
  262.     # the listbox (and possibly scrollbar)
  263.     # geometry to the popdown toplevel.
  264.  
  265.     # Get the geometry of the linked entry widget
  266.  
  267.     set x [winfo rootx $m_ent]
  268.     set y [winfo rooty $m_ent]
  269.     set h [winfo height $m_ent]
  270.     set w [winfo width $m_ent]
  271.  
  272.     # Place the popdown below the entry if there's space, otherwise
  273.     # above it.
  274.  
  275.     set H [winfo reqheight $m_popdownFrame]
  276.     if {$y + $h + $H < [winfo screenheight $m_popdownFrame]} {
  277.         set Y [expr $y + $h]
  278.     } else {
  279.         set Y [expr $y - $H]
  280.     }
  281.  
  282.     wm geometry $m_popdownToplevel ${w}x${H}+$x+$Y
  283.     wm transient $m_popdownToplevel [winfo toplevel $m_ent]
  284.     # ttk::combobox only does this for x11
  285.     # and win32, but we don't support other
  286.     # windowing systems
  287.  
  288.     # ttk::combobox does
  289.     #    wm attribute                     \
  290.     #              $m_popdownToplevel     \
  291.     #                   -topmost 1
  292.     # at this point, but this appears to put
  293.     # a global grab on it (at least on UNIX)
  294.     # This is VERY bad news.
  295.     #       wm attribute $m_popdownToplevel -topmost 1
  296.     wm deiconify $m_popdownToplevel
  297.     raise $m_popdownToplevel
  298.     }
  299.  
  300.     #----------------------------------------------------------------------
  301.     #
  302.     # SetEntryFromListbox
  303.     #
  304.     #----------------------------------------------------------------------
  305.  
  306.     method SetEntryFromListbox {} {
  307.     set vals [set [$m_popdownListbox cget -listvariable]]
  308.  
  309.     set seln 0
  310.     for {set i 0} {$i < [llength $vals]} {incr i -1} {
  311.         if {[$m_popdownListbox selection includes $i]} {
  312.         set seln $i
  313.         break
  314.         }
  315.     }
  316.  
  317.     $m_ent delete 0 end
  318.     $m_ent insert 0 [lindex $vals $seln]
  319.     }
  320.  
  321.     method Unpost {} {
  322.     if {[winfo exists $m_popdownToplevel]} {
  323.         wm withdraw $m_popdownToplevel
  324.         # ttk::combobox also does a grab release
  325.         # but we aren't messing with grabs.
  326.     }
  327.     }
  328. }
  329.  
  330. ###############################################################################
  331. #
  332. # Some utility routines for debugging
  333. #
  334. ###############################################################################
  335.  
  336. proc showbindings {w {chn stdout}} {
  337.     foreach evt [bind $w] {
  338.     puts $chn ""
  339.     puts $chn $evt
  340.     puts $chn
  341.     puts $chn [bind $w $evt]
  342.     }
  343. }
  344.  
  345. proc showwindows {w {indent 0} {chn stdout}} {
  346.     showwindowsrecursively $w $indent $chn
  347.  
  348.     puts $chn ""
  349.     puts $chn "focus: [focus]"
  350.     puts $chn "grabs: [grab current]"
  351. }
  352.  
  353. proc scheduleshowbindings {w} {
  354.     showbindings $w
  355.  
  356.     after 5000 scheduleshowbindings $w
  357. }
  358.  
  359. proc scheduleshowwindows {w} {
  360.     showwindows $w
  361.  
  362.     after 5000 scheduleshowwindows $w
  363. }
  364.  
  365. proc showwindowsrecursively {w indent chn} {
  366.     puts $chn [string repeat " " $indent]$w:\ [winfo class $w]
  367.     set newIndent [expr $indent + 4]
  368.     foreach child [winfo children $w] {
  369.     showwindowsrecursively $child $newIndent $chn
  370.     }
  371. }
  372.  
  373. proc writebindings {w fnm} {
  374.     set fil [open $fnm w]
  375.     showbindings $w $fil
  376.     close $fil
  377. }
  378.  
  379. proc writewindows {w fnm} {
  380.     set fil [open $fnm w]
  381.     showwindows $w 0 $fil
  382.     close $fil
  383. }
  384.  
  385. ###############################################################################
  386. #
  387. # mainline code
  388. #
  389. ###############################################################################
  390.  
  391. if {[catch {set retain [winfo viewable .]}]} {
  392.     set retain no
  393. }
  394. package require Tk
  395. if {!$retain} {
  396.     wm withdraw .
  397. }
  398.  
  399. # Make a uniquely named toplevel
  400.  
  401. for {set i 0} {[winfo exists .tl$i]} {incr i} {
  402. }
  403. set tl [toplevel .tl$i]
  404. wm title $tl "entryWithCompletion demo v[lindex $HISTORY end-3]"
  405.  
  406. # Create a frame with three labelled entry widgets gridded into it
  407.  
  408. set frm [ttk::frame $tl.frm]
  409. pack $frm -fill both -expand yes
  410.  
  411. array set label {0 "Dummy entry" 1 "State" 2 "Dummy entry"}
  412.  
  413. for {set i 0} {$i < 3} {incr i} {
  414.     set lbl [ttk::label $frm.l$i -text $label($i)]
  415.     grid $lbl -row $i -column 0 -sticky nsew
  416.     set ent  [ttk::entry $frm.e$i]
  417.     grid $ent -row $i -column 1 -sticky nsew
  418. }
  419.  
  420. # Add a menu with File > Exit command
  421.  
  422. set mnu [menu $tl.mnu]
  423. $tl configure -menu $mnu
  424.  
  425. set m [menu $mnu.file -tearoff no]
  426. $mnu add cascade -label File -underline 0 -menu $m
  427.  
  428. $m add command -label Exit -underline 1 -command [list destroy $tl]
  429.  
  430. # Make a new hand-crafted combobox, associated with the second entry widget
  431.  
  432. entryWithCompletion new $frm.e1 {
  433.     Alabama Alaska Arizona Arkansas California
  434.     Colorado Connecticut Delaware Florida Georgia
  435.     Hawaii Idaho Illinois Indiana Iowa
  436.     Kansas Kentucky Louisiana Maine Maryland
  437.     Massachusetts Michigan Minnesota Mississippi Missouri
  438.     Montana Nebraska Nevada "New Hampshire" "New Jersey"
  439.     "New Mexico" "New York" "North Carolina" "North Dakota" Ohio
  440.     Oklahoma Oregon Pennsylvania "Rhode Island" "South Carolina"
  441.     "South Dakota" Tennessee Texas Utah Vermont
  442.     Virginia Washington "West Virginia" Wisconsin Wyoming
  443. }
  444.  
  445. focus $frm.e0
  446.  
  447. # Set up to exit after a minute
  448.  
  449. if {$tcl_interactive} {
  450.     after 60000 destroy $tl
  451. } else {
  452.     after 60000 exit
  453. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement