#!/bin/sh # the next line restarts using wish \ exec wish "$0" ${1+"$@"} # widget -- # This script demonstrates the various widgets provided by Tk, along with many # of the features of the Tk toolkit. This file only contains code to generate # the main window for the application, which invokes individual # demonstrations. The code for the actual demonstrations is contained in # separate ".tcl" files is this directory, which are sourced by this script as # needed. package require tk package require msgcat destroy {*}[winfo children .] set tk_demoDirectory [file join [pwd] [file dirname [info script]]] ::msgcat::mcload $tk_demoDirectory namespace import ::msgcat::mc wm title . [mc "Widget Demonstration"] if {[tk windowingsystem] eq "x11"} { # This won't work everywhere, but there's no other way in core Tk at the # moment to display a coloured icon. image create photo TclPowered \ -file [file join $tk_library images logo64.gif] wm iconwindow . [toplevel ._iconWindow] pack [label ._iconWindow.i -image TclPowered] wm iconname . [mc "tkWidgetDemo"] } if {"defaultFont" ni [font names]} { # TIP #145 defines some standard named fonts if {"TkDefaultFont" in [font names] && "TkFixedFont" in [font names]} { # FIX ME: the following technique of cloning the font to copy it works # fine but means that if the system font is changed by Tk # cannot update the copied font. font alias might be useful # here -- or fix the app to use TkDefaultFont etc. font create mainFont {*}[font configure TkDefaultFont] font create fixedFont {*}[font configure TkFixedFont] font create boldFont {*}[font configure TkDefaultFont] -weight bold font create titleFont {*}[font configure TkDefaultFont] -weight bold font create statusFont {*}[font configure TkDefaultFont] font create varsFont {*}[font configure TkDefaultFont] if {[tk windowingsystem] eq "aqua"} { font configure titleFont -size 17 } } else { font create mainFont -family Helvetica -size 12 font create fixedFont -family Courier -size 10 font create boldFont -family Helvetica -size 12 -weight bold font create titleFont -family Helvetica -size 18 -weight bold font create statusFont -family Helvetica -size 10 font create varsFont -family Helvetica -size 14 } } set widgetDemo 1 set font mainFont # The SVG images used below are based on some icons provided by the # official open source SVG icon library for the Bootstrap project, # licensed under the MIT license (https://opensource.org/licenses/MIT). # # See https://github.com/twbs/icons. set viewData { } set refreshData { } set printData { } proc images {arg} { set fgColor [ttk::style lookup . -foreground {} black] lassign [winfo rgb . $fgColor] r g b set fgColor [format "#%02x%02x%02x" \ [expr {$r >> 8}] [expr {$g >> 8}] [expr {$b >> 8}]] foreach action {view refresh print} { upvar ${action}Data imgData for {set data $imgData; set startIdx 0} \ {[set idx1 [string first "#000000" $data $startIdx]] >= 0} \ {set startIdx [expr {$idx1 + 7}]} { set idx2 [expr {$idx1 + 6}] set data [string replace $data $idx1 $idx2 $fgColor] } switch $arg { create { image create photo ::img::$action -format $::tk::svgFmt \ -data $data } configure { ::img::$action configure -data $data } } } } images create set mainClass [winfo class .] foreach event {<> <> <>} { bind $mainClass $event { images configure } } unset mainClass event image create photo ::img::delete -format $::tk::svgFmt -data { } #---------------------------------------------------------------- # The code below creates the main window, consisting of a menu bar and a text # widget that explains how to use the program, plus lists all of the demos as # hypertext items. #---------------------------------------------------------------- menu .menuBar -tearoff 0 # On Aqua, just use the default menu. if {[tk windowingsystem] ne "aqua"} { # This is a tk-internal procedure to make i18n easier ::tk::AmpMenuArgs .menuBar add cascade -label [mc "&File"] \ -menu .menuBar.file menu .menuBar.file -tearoff 0 ::tk::AmpMenuArgs .menuBar.file add command -label [mc "&About..."] \ -command {tkAboutDialog} -accelerator [mc ""] bind . {tkAboutDialog} .menuBar.file add sep if {[string match win* [tk windowingsystem]]} { # Windows doesn't usually have a Meta key ::tk::AmpMenuArgs .menuBar.file add command -label [mc "&Quit"] \ -command {exit} -accelerator [mc "Ctrl+Q"] bind . <[mc "Control-q"]> {exit} } else { ::tk::AmpMenuArgs .menuBar.file add command -label [mc "&Quit"] \ -command {exit} -accelerator [mc "Meta-Q"] bind . <[mc "Meta-q"]> {exit} } . configure -menu .menuBar } ttk::frame .statusBar ttk::label .statusBar.lab -text " " -anchor w if {[tk windowingsystem] eq "aqua"} { ttk::separator .statusBar.sep pack .statusBar.sep -side top -expand yes -fill x -pady 0 } pack .statusBar.lab -side left -padx 1.5p -expand yes -fill both if {[tk windowingsystem] ne "aqua"} { ttk::sizegrip .statusBar.foo pack .statusBar.foo -side left -padx 1.5p } pack .statusBar -side bottom -fill x -pady 1.5p set textheight 30 catch { set textheight [expr { ([winfo screenheight .] * 0.7) / [font metrics mainFont -displayof . -linespace] }] } ttk::frame .textFrame ttk::scrollbar .s -orient vertical -command {.t yview} -takefocus 1 pack .s -in .textFrame -side right -fill y text .t -yscrollcommand {.s set} -wrap word -width 70 -height $textheight \ -font mainFont -setgrid 1 -highlightthickness 0 \ -padx 3p -pady 1.5p -takefocus 0 pack .t -in .textFrame -expand y -fill both -padx 1 pack .textFrame -expand yes -fill both if {[tk windowingsystem] eq "aqua"} { pack configure .statusBar.lab -padx {10 18} -pady {4 6} pack configure .statusBar -pady 0 .t configure -padx 10 -pady 0 } # Create a bunch of tags to use in the text widget, such as those for section # titles and demo descriptions. Also define the bindings for tags. .t tag configure title -font titleFont .t tag configure subtitle -font titleFont .t tag configure bold -font boldFont if {[tk windowingsystem] eq "aqua"} { .t tag configure title -spacing1 8 .t tag configure subtitle -spacing3 3 } # We put some "space" characters to the left and right of each demo # description so that the descriptions are highlighted only when the mouse # cursor is right over them (but not when the cursor is to their left or # right). # .t tag configure demospace -lmargin1 1c -lmargin2 1c if {[winfo depth .] == 1} { .t tag configure demo -lmargin1 1c -lmargin2 1c \ -underline 1 .t tag configure visited -lmargin1 1c -lmargin2 1c \ -underline 1 .t tag configure hot -background black -foreground white } else { .t tag configure demo -lmargin1 1c -lmargin2 1c \ -foreground blue -underline 1 .t tag configure visited -lmargin1 1c -lmargin2 1c \ -foreground #303080 -underline 1 if {[tk windowingsystem] eq "aqua"} { .t tag configure demo -foreground systemLinkColor .t tag configure visited -foreground purple } .t tag configure hot -foreground red -underline 1 } # The tag "new" must be the one having the highest priority. # .t tag configure new -foreground #c00000 -underline 0 -font boldFont .t tag bind demo { invoke [.t index {@%x,%y}] } set lastLine "" .t tag bind demo { set lastLine [.t index {@%x,%y linestart}] .t tag add hot "$lastLine +1 chars" "$lastLine lineend -1 chars" .t config -cursor [::ttk::cursor link] showStatus [.t index {@%x,%y}] } .t tag bind demo { .t tag remove hot 1.0 end .t config -cursor [::ttk::cursor text] .statusBar.lab config -text "" } .t tag bind demo { set newLine [.t index {@%x,%y linestart}] if {$newLine ne $lastLine} { .t tag remove hot 1.0 end set lastLine $newLine set tags [.t tag names {@%x,%y}] set i [lsearch -glob $tags demo-*] if {$i >= 0} { .t tag add hot "$lastLine +1 chars" "$lastLine lineend -1 chars" } } showStatus [.t index {@%x,%y}] } ############################################################################## # Create the text for the text widget. # addFormattedText -- # # Add formatted text (but not hypertext) to the text widget after first # passing it through the message catalog to allow for localization. # Lines starting with @@ are formatting directives (insert title, insert # demo hyperlink, begin newline, or change style) and all other lines # are literal strings to be inserted. Substitutions are performed, # allowing processing pieces through the message catalog. Blank lines # are ignored. # proc addFormattedText {formattedText} { set style normal set isNL 1 set demoCount 0 set new 0 foreach line [split $formattedText \n] { set line [string trim $line] if {$line eq ""} { continue } if {[string match @@* $line]} { set data [string range $line 2 end] set key [lindex $data 0] set values [lrange $data 1 end] switch -exact -- $key { title { .t insert end [mc $values]\n title \n normal } newline { .t insert end \n $style set isNL 1 } subtitle { .t insert end "\n" {} [mc $values] subtitle \ " \n " demospace set demoCount 0 } demo { set description [lassign $values name] .t insert end "[incr demoCount]. [mc $description]" \ [list demo demo-$name] if {$new} { .t insert end " [mc NEW]" new set new 0 } .t insert end " \n " demospace } new { set new 1 } default { set style $key } } continue } if {!$isNL} { .t insert end " " $style } set isNL 0 .t insert end [mc $line] $style } } addFormattedText { @@title Tk Widget Demonstrations This application provides a front end for several short scripts that demonstrate what you can do with Tk widgets. Each of the numbered lines below describes a demonstration; you can click on it to invoke the demonstration. Once the demonstration window appears, you can click the @@bold See Code @@normal button to see the Tcl/Tk code that created the demonstration. If you wish, you can edit the code and click the @@bold Rerun Demo @@normal button in the code window to reinvoke the demonstration with the modified code. @@newline @@subtitle Labels, buttons, checkbuttons, and radiobuttons @@demo label Labels (text and bitmaps) @@demo unicodeout Labels and UNICODE text @@demo button Buttons @@demo check Check-buttons (select any of a group) @@demo radio Radio-buttons (select one of a group) @@demo puzzle A 15-puzzle game made out of buttons @@demo icon Iconic buttons that use bitmaps @@demo image1 Two labels displaying images @@demo image2 A simple user interface for viewing images @@demo labelframe Labelled frames @@demo ttkbut The simple Themed Tk widgets } if {[tk windowingsystem] eq "aqua"} { addFormattedText { @@subtitle Mac-Specific Widgets and Window Styles @@new @@demo mac_styles Special widgets for macOS @@new @@demo mac_wm Window styles for macOS @@new @@demo mac_tabs Tabbed Windows on macOS } } addFormattedText { @@subtitle Listboxes and Trees @@demo states The 50 states @@demo colors Colors: change the color scheme for the application @@demo sayings A collection of famous and infamous sayings @@demo mclist A multi-column list of countries @@demo tree A directory browser tree @@subtitle Entries, Spin-boxes and Combo-boxes @@demo entry1 Entries without scrollbars @@demo entry2 Entries with scrollbars @@demo entry3 Validated entries and password fields @@demo spin Spin-boxes @@demo combo Combo-boxes @@demo form Simple Rolodex-like form @@subtitle Text @@demo text Basic editable text @@demo style Text display styles @@demo bind Hypertext (tag bindings) @@demo twind A text widget with embedded windows and other features @@demo search A search tool built with a text widget @@demo textpeer Peering text widgets @@subtitle Canvases @@demo items The canvas item types @@demo plot A simple 2-D plot @@demo ctext Text items in canvases @@demo arrow An editor for arrowheads on canvas lines @@demo ruler A ruler with adjustable tab stops @@demo floor A building floor plan @@demo cscroll A simple scrollable canvas @@demo knightstour A Knight's tour of the chess board @@subtitle Scales and Progress Bars @@demo hscale Horizontal scale @@demo vscale Vertical scale @@demo ttkscale Themed scale linked to a label with traces @@demo ttkprogress Progress bar @@subtitle Paned Windows and Notebooks @@demo paned1 Horizontal paned window @@demo paned2 Vertical paned window @@demo ttkpane Themed nested panes @@demo ttknote Notebook widget @@subtitle Menus and Toolbars @@demo menu Menus and cascades (sub-menus) @@demo menubu Menu-buttons @@demo ttkmenu Themed menu buttons @@demo toolbar Themed toolbar @@subtitle Common Dialogs @@demo msgbox Message boxes @@demo filebox File selection dialog @@demo clrpick Color picker @@demo fontchoose Font selection dialog @@new @@demo systray System tray icon and notification @@new @@demo print Printing from canvas and text widgets @@subtitle Animation @@demo anilabel Animated labels @@demo aniwave Animated wave @@demo pendulum Pendulum simulation @@demo goldberg A celebration of Rube Goldberg @@subtitle Miscellaneous @@demo bitmap The built-in bitmaps @@demo dialog1 A dialog box with a local grab @@demo dialog2 A dialog box with a global grab @@new @@demo windowicons Window icons and badges } ############################################################################## .t configure -state disabled focus .s # addSeeDismiss -- # Add "See Code" and "Dismiss" button frame, with optional "See Vars" # # Arguments: # w - The name of the frame to use. proc addSeeDismiss {w show {vars {}} {extra {}}} { ## See Code / Dismiss buttons ttk::frame $w #ttk::frame $w.sep -height 2 -relief sunken ttk::separator $w.sep grid $w.sep -columnspan 4 -row 0 -sticky ew -pady 1.5p ttk::button $w.dismiss -text [mc "Dismiss"] \ -image ::img::delete -compound left \ -command [list destroy [winfo toplevel $w]] ttk::button $w.code -text [mc "See Code"] \ -image ::img::view -compound left \ -command [list showCode $show] set buttons [list x $w.code $w.dismiss] if {[llength $vars]} { ttk::button $w.vars -text [mc "See Variables"] \ -image ::img::view -compound left \ -command [concat [list showVars $w.dialog] $vars] set buttons [linsert $buttons 1 $w.vars] } if {$extra ne ""} { set buttons [linsert $buttons 1 [uplevel 1 $extra]] } grid {*}$buttons -padx 3p -pady 3p grid columnconfigure $w 0 -weight 1 if {[tk windowingsystem] eq "aqua"} { foreach b [lrange $buttons 1 end] {$b configure -takefocus 0} grid configure $w.sep -pady 0 grid configure {*}$buttons -pady {10 12} grid configure [lindex $buttons 1] -padx {16 4} grid configure [lindex $buttons end] -padx {4 18} } return $w } # positionWindow -- # This procedure is invoked by most of the demos to position a new demo # window. # # Arguments: # w - The name of the window to position. proc positionWindow w { wm geometry $w +300+300 } # showVars -- # Displays the values of one or more variables in a window, and updates the # display whenever any of the variables changes. # # Arguments: # w - Name of new window to create for display. # args - Any number of names of variables. proc showVars {w args} { catch {destroy $w} toplevel $w if {[tk windowingsystem] eq "x11"} {wm attributes $w -type dialog} wm title $w [mc "Variable values"] set b [ttk::frame $w.frame] grid $b -sticky news set f [ttk::labelframe $b.title -text [mc "Variable values:"]] foreach var $args { ttk::label $f.n$var -text "$var:" -anchor w ttk::label $f.v$var -textvariable $var -anchor w grid $f.n$var $f.v$var -padx 1.5p -pady 1.5p -sticky w } ttk::button $b.ok -text [mc "OK"] \ -command [list destroy $w] -default active bind $w [list $b.ok invoke] bind $w [list $b.ok invoke] grid $f -sticky news -padx 3p grid $b.ok -sticky e -padx 3p -pady {4.5p 3p} if {[tk windowingsystem] eq "aqua"} { $b.ok configure -takefocus 0 grid configure $b.ok -pady {10 12} -padx {16 18} grid configure $f -padx 10 -pady {10 0} } grid columnconfig $f 1 -weight 1 grid rowconfigure $f 100 -weight 1 grid columnconfig $b 0 -weight 1 grid rowconfigure $b 0 -weight 1 grid columnconfig $w 0 -weight 1 grid rowconfigure $w 0 -weight 1 } # invoke -- # This procedure is called when the user clicks on a demo description. It is # responsible for invoking the demonstration. # # Arguments: # index - The index of the character that the user clicked on. proc invoke index { global tk_demoDirectory set tags [.t tag names $index] set i [lsearch -glob $tags demo-*] if {$i < 0} { return } set cursor [.t cget -cursor] .t configure -cursor [::ttk::cursor busy] update set demo [string range [lindex $tags $i] 5 end] uplevel 1 [list source -encoding utf-8 [file join $tk_demoDirectory $demo.tcl]] update .t configure -cursor $cursor .t tag add visited "$index linestart +1 chars" "$index lineend -1 chars" } # showStatus -- # # Show the name of the demo program in the status bar. This procedure is # called when the user moves the cursor over a demo description. # proc showStatus index { set tags [.t tag names $index] set i [lsearch -glob $tags demo-*] set cursor [.t cget -cursor] if {$i < 0} { .statusBar.lab config -text " " set newcursor [::ttk::cursor text] } else { set demo [string range [lindex $tags $i] 5 end] .statusBar.lab config -text [mc "Run the \"%s\" sample program" $demo] set newcursor [::ttk::cursor link] } if {$cursor ne $newcursor} { .t config -cursor $newcursor } } # evalShowCode -- # # Arguments: # w - Name of text widget containing code to eval proc evalShowCode {w} { set code [$w get 1.0 end-1c] uplevel #0 $code } # showCode -- # This procedure creates a toplevel window that displays the code for a # demonstration and allows it to be edited and reinvoked. # # Arguments: # w - The name of the demonstration's window, which can be used to # derive the name of the file containing its code. proc showCode w { global tk_demoDirectory set file [string range $w 1 end].tcl set top .code if {![winfo exists $top]} { toplevel $top if {[tk windowingsystem] eq "x11"} {wm attributes $top -type dialog} set t [frame $top.f] set text [text $t.text -font fixedFont -height 24 -wrap word \ -xscrollcommand [list $t.xscroll set] \ -yscrollcommand [list $t.yscroll set] \ -setgrid 1 -highlightthickness 0 -padx 3p -pady 1.5p \ -tabstyle wordprocessor] ttk::scrollbar $t.xscroll -command [list $t.text xview] \ -orient horizontal ttk::scrollbar $t.yscroll -command [list $t.text yview] \ -orient vertical grid $t.text $t.yscroll -sticky news #grid $t.xscroll grid rowconfigure $t 0 -weight 1 grid columnconfig $t 0 -weight 1 set btns [ttk::frame $top.btns] ttk::separator $btns.sep grid $btns.sep -columnspan 4 -row 0 -sticky ew -pady 1.5p ttk::button $btns.dismiss -text [mc "Dismiss"] \ -default active -command [list destroy $top] \ -image ::img::delete -compound left ttk::button $btns.print -text [mc "Print Code"] \ -command [list printCode $text $file] \ -image ::img::print -compound left ttk::button $btns.rerun -text [mc "Rerun Demo"] \ -command [list evalShowCode $text] \ -image ::img::refresh -compound left set buttons [list x $btns.rerun $btns.print $btns.dismiss] grid {*}$buttons -padx 3p -pady 3p grid columnconfigure $btns 0 -weight 1 if {[tk windowingsystem] eq "aqua"} { foreach b [lrange $buttons 1 end] {$b configure -takefocus 0} grid configure $btns.sep -pady 0 grid configure {*}$buttons -pady {10 12} grid configure [lindex $buttons 1] -padx {16 4} grid configure [lindex $buttons end] -padx {4 18} } grid $t -sticky news grid $btns -sticky ew grid rowconfigure $top 0 -weight 1 grid columnconfig $top 0 -weight 1 bind $top { if {[winfo class %W] ne "Text"} { .code.btns.dismiss invoke } } bind $top [bind $top ] } else { wm deiconify $top raise $top } wm title $top [mc "Demo code: %s" [file join $tk_demoDirectory $file]] wm iconname $top $file set id [open [file join $tk_demoDirectory $file]] fconfigure $id -encoding utf-8 -eofchar "\032 {}" $top.f.text delete 1.0 end $top.f.text insert 1.0 [read $id] $top.f.text mark set insert 1.0 close $id } # printCode -- # Prints the source code currently displayed in the See Code dialog. Much # thanks to Arjen Markus for this. # # Arguments: # w - Name of text widget containing code to print # file - Name of the original file (implicitly for title) proc printCode {w file} { tk print $w } # tkAboutDialog -- # # Pops up a message box with an "about" message # proc tkAboutDialog {} { tk_messageBox -icon info -type ok -title [mc "About Widget Demo"] \ -message [mc "Tk widget demonstration application"] -detail \ "[mc "Copyright © %s" {1996-1997 Sun Microsystems, Inc.}] [mc "Copyright © %s" {1997-2000 Ajuba Solutions, Inc.}] [mc "Copyright © %s" {2001-2009 Donal K. Fellows}] [mc "Copyright © %s" {2002-2007 Daniel A. Steffen}] [mc "Copyright © %s" {2021 Kevin Walzer}]" } # Local Variables: # mode: tcl # End: