jnc Dec 2, 2009 - Tk currently lacks a multiline entry widget. The text widget is a great widget but a bit complex if you just need a multiline plain text widget that acts like entry.
This is one of many solutions on the wiki. This solution includes support for:
snit::widget multiline_entry {
delegate option * to text
delegate method * to text
# On/Off options
option -yscroll -default yes
option -xscroll -default no
option -allowtab -default no
option -readonly -default no
# Miscellaneous options
option -textvariable -default 0
constructor { args } {
install text using text $win.txt
grid $win.txt -row 0 -column 0 -sticky nswe
$self configurelist $args
if { [$win cget -yscroll] } {
$win.txt configure -yscrollcommand [list $win.vsb set]
ttk::scrollbar $win.vsb -command [list $win.txt yview]
grid $win.vsb -row 0 -column 1 -sticky nsw
}
if { [$win cget -xscroll] } {
$win.txt configure -xscrollcommand [list $win.hsb set]
ttk::scrollbar $win.hsb -orient horizontal -command [list $win.txt xview]
grid $win.hsb -row 1 -column 0 -sticky we
}
grid rowconfigure $win 0 -weight 1
grid columnconfigure $win 0 -weight 1
if {[$win cget -textvariable] != 0} {
set varName [$win cget -textvariable]
upvar 3 $varName v
$win.txt insert 1.0 $v
$win.txt mark set insert 1.0
trace add variable v write [list $self setContent]
trace add variable v read [list $self getContent]
}
bind $win <FocusIn> [list focus $win.txt]
if { !$options(-allowtab) } {
bind $win.txt <Shift-Tab> [list $self focusPrev]
bind $win.txt <Tab> [list $self focusNext]
}
if { $options(-readonly) } {
bind $win.txt <KeyPress> break
bind $win.txt <ButtonRelease-2> break
bind $win.txt <Down> continue
bind $win.txt <Up> continue
bind $win.txt <Prior> continue
bind $win.txt <Next> continue
bind $win.txt <Left> continue
bind $win.txt <Right> continue
}
}
method getTextWidget {} {
return $win.txt
}
method focusPrev {} {
focus [tk_focusPrev $win]
return -code break
}
method focusNext {} {
focus [tk_focusNext $win.txt]
return -code break
}
method setContent { name element op} {
upvar 1 $name x
$win.txt delete 1.0 end
if { [array exists x] } {
$win.txt insert 1.0 $x($element)
} else {
$win.txt insert 1.0 $x
}
}
method getContent { name element op } {
upvar 1 $name x
if { [array exists x] } {
set x($element) [$win.txt get 1.0 "end-1 char"]
} else {
set x [$win.txt get 1.0 "end-1 char"]
}
}
}jnc Dec 21, 2009 - Added a -readonly option, however, it also prevents keyboard navigation. Any better ideas?
WHD Redefine the "insert" and "delete" subcommands so that they do nothing if -readonly is true. Then, the Text bindings that change the content of the widget will have no effect. To be really fancy, define your own -state option with three values, "normal", "disabled", and "readonly"; pass the first two along to the hull and set your readonly flag for the third.
Bezoar I rewrote the above widget using TclOO Multi-line TclOO Text Entry Widget just to see how hard it would be to convert between the two. Turns out it was very easy. I did add some features that don't exist in this version. First all option not recognized by the Widget get sent to the internal text widget so you get the -state option mentioned above by WHD for free. I also corrected the fact that you could still cut and paste into a readonly widget and finally when -xscroll = 1 I also shut off wrapping in the text widget otherwise it is useless. I have some test code at the afore mentioned page to test the widget.
jnc Sep 15, 2010 - Updated code, includes better readonly support (keyboard navigation) and no mouse paste ability. Handling of getting/setting via array.