WJG (25th August, 2005) I thought that it would be useful to 'collapse' or 'fold' a selected range of content in a text widget. So, this is snippet I came up with.
#---------------------------------------------------------------- # TextCollapser.tcl #---------------------------------------------------------------- # Create by William J Giddings, 2005 # # Notes: # ------ # Blocks can be nested. # # Use: # ---- # Enter text then select a region with click and mouse drag. # Click the button in the toolbar. # The first line will have a toggle button, the 'sub-text' will be coloured. # To remove the folding effect simply delete the toggle button # #---------------------------------------------------------------- #---------------------------------------------------------------- # application namespace #---------------------------------------------------------------- namespace eval collapser { # tag base name set tagbase cb_ # button data image create photo collapser::downarrow \ -data R0lGODlhDAAMAJEAAP///9TQyAAAAAAAACwAAAAADAAMAAACEIyPqcudAqNQcq7orNu8qwIAOw== image create photo collapser::uparrow \ -data R0lGODlhDAAMAJEAAP///9TQyAAAAAAAACwAAAAADAAMAAACEoyPqcsobcRrcq5qU6VZdQgmBQA7 image create photo collapser::button \ -data R0lGODlhDAAMAJEAAP////4AAAAAAAAAACwAAAAADAAMAAACGoSPJ8ttDUWaJ9iLDd5B8+t9HTVFDmAyZFIAADs= } #---------------------------------------------------------------- # obtain a unique name for a new block of text #---------------------------------------------------------------- proc collapser::name {path} { llapser::name {path} { # get a sorted list of the current windows set windowlist "" foreach {a b c} [$path dump -window 1.0 end] { if { [string first $path.$collapser::tagbase $b 0 ] != "-1" } { lappend windowlist $b } } set windowlist [lsort $windowlist] set windowlist [lsort $windowlist] # get the last one the list set next [lindex $windowlist end] # increment the number and return the new tag name set j [string trimleft $next $path.${collapser::tagbase}] if {$j == "" } {set j 0} return ${collapser::tagbase}[incr j] } #---------------------------------------------------------------- # add a toolbar button #---------------------------------------------------------------- proc collapser::addbutton {path} { button $path -text Block -borderwidth 0 -command collapser::block -image collapser::button pack $path -side left } #---------------------------------------------------------------- # create a new collapsable text block #---------------------------------------------------------------- proc collapser::block {} { # get path to active widget set path [focus] # only works on text widgets if {[winfo class $path]!="Text"} {return} # get tag name for the new block set tag [collapser::name $path] set tag [collapser::name $path] # create and configure new tag $path tag add $tag {sel.first lineend} sel.last $path tag configure $tag -foreground red # create block toggle button button $path.$tag \ -borderwidth 0 \ -text {-} \ -image collapser::downarrow \ -command "collapser::toggle $path $tag" # add the button to the text $path window create {insert linestart} -window $path.$tag $path window create {insert linestart} -window $path.$tag # remove text tagging if the button is deleted bind $path.$tag <Destroy> {$path] tag delete $tag} } #---------------------------------------------------------------- # toggle tag settings #---------------------------------------------------------------- proc collapser::toggle {path tag} { if {[$path tag cget $tag -elide] == 1} { $path tag configure $tag -elide 0 $path.$tag configure -text - -image collapser::downarrow } else { $path tag configure $tag -elide 1 $path.$tag configure -text + -image collapser::uparrow } } #---------------------------------------------------------------- # demo code to test the package #---------------------------------------------------------------- proc demo {} { console show frame .fr pack .fr -fill x text .txt -font {Ariel 12} pack .txt -fill both -expand 1 focus .txt collapser::addbutton .fr.b_1 } #---------------------------------------------------------------- # the ubiquitious demo! #---------------------------------------------------------------- demo
escargo 26 Aug 2005 - I got this message deleting a tag:
can't read "path": no such variable can't read "path": no such variable while executing "$path] tag delete $tag" (command bound to event)
I deleted the tag by selecting it and typing control-x on Windows XP Pro. When a tag is deleted, it appears that the text associated with the tag is also deleted.
It would be nice if the cursor shape could change over the tag.
LV on line 97 of this file, it looks like this bind is wrong
bind $path.$tag <Destroy> {$path] tag delete $tag}
I don't see a reason for that ] in the command.
See also Text widget elision