First, we destroy a toplevel with the same name as given for our inputbox, if it exists. The argument default will be used for setting in advance a value to our entry widget. Next, we create the toplevel for our inputbox, giving it the class "Inputbox", icon name, and make sure any WM_DELETE_WINDOW messages from the window manager will be honored (doing nothing). The global frmReturn will store the return value of the procedure. It needs to be global because of the way widget bindings work.
proc inputbox {w text {default {}}} { global frmReturn catch {destroy $w} toplevel $w -class Inputbox wm iconname $w Inputbox wm protocol $w WM_DELETE_WINDOW { }
Then we create a frame for the top and bottom regions of the inputbox. The first will contain the message and the input entry. The other will have the buttons with commands for setting the frmReturn variable with the entry value or a null string.
wm transient $w 1 pack [frame $w.bot -relief raised -bd 1] -side bottom -fill both pack [frame $w.top -relief raised -bd 1] -side top -fill both -expand 1 option add *Inputbox.msg.wrapLength 3i widgetDefault label $w.msg -justify left -text $text -font {Times 18} entry $w.entry pack $w.msg -in $w.top -side top -expand 1 -fill both -padx 3m -pady 3m pack $w.entry -in $w.top -side top -expand 1 -fill x -padx 3m -pady 3m grid [button $w.b0 -text ok -command "set frmReturn \[$w.entry get\]"] \ -in $w.bot -column 0 -row 0 -stick sw -padx 10 grid [button $w.b1 -text cancel -command {set frmReturn ""}] \ -in $w.bot -column 1 -row 0 -stick sw -padx 10
If a default value is given we insert it at the entry. We put <Return> as a binding to the entry, so the user has only to type what he wants and press the carriage return key to finish. Next, we calculate the center of the screen, save the current focus and grab status and make our settings. Then we remain waiting for a frmReturn variable change.
$w.entry insert end $default bind $w.entry <Return> "$w.b0 invoke" bind $w <Destroy> {set frmReturn {}} wm withdraw $w update idletasks set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2\ - [winfo vrootx [winfo parent $w]]] set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2\ - [winfo vrooty [winfo parent $w]]] wm geometry $w +$x+$y wm deiconify $w set oldfocus [focus] set oldgrab [grab current $w] if {$oldgrab != ""} { set grabstatus [grab status $oldgrab] } grab $w focus $w.entry tkwait variable frmReturn
When the variable was modified, we arrive here, at the last part of the procedure. We have only to restore the old focus and grab status and return the string stored in frmReturn.
catch {focus $oldfocus} catch { bind $w Destroy {} destroy $w } if {$oldgrab != ""} { if {$grabstatus == "global"} { grab -global $oldgrab } else { grab $oldgrab } } return $frmReturn }
The simple commands below show how to test it:
set name [inputbox .x "Please input your name:"] puts "Hello $name, nice to meet you!"