#!/usr/bin/env ruby # # Demo for 'tile' package. # require 'tk' demodir = File.dirname($0) Tk::AUTO_PATH.lappend('.', demodir, File.join(demodir, 'themes')) require 'tkextlib/tile' def version?(ver) TkPackage.vcompare(Tk::Tile.package_version, ver) >= 0 end Tk.load_tclscript(File.join(demodir, 'toolbutton.tcl')) Tk.load_tclscript(File.join(demodir, 'repeater.tcl')) # This forces an update of the available packages list. It's required # for package names to find the themes in demos/themes/*.tcl ## Tk.tk_call(TkPackage.unknown_proc, 'Tcl', TkPackage.provide('Tcl')) ## --> This doesn't work. ## Because, unknown_proc may be "command + some arguments". Tk.ip_eval("#{TkPackage.unknown_proc} Tcl #{TkPackage.provide('Tcl')}") TkRoot.new{ title 'Tile demo' iconname 'Tile demo' } # The descriptive names of the builtin themes. $THEMELIST = [ ['default', 'Classic'], ['alt', 'Revitalized'], ['winnative', 'Windows native'], ['xpnative', 'XP Native'], ['aqua', 'Aqua'], ] $V = TkVariable.new_hash(:THEME => 'default', :COMPOUND => 'top', :CONSOLE => false, :MENURADIO1 => 'One', :MENUCHECK1 => true, :PBMODE => 'determinate', :SELECTED => true, :CHOICE => 2) # Add in any available loadable themes. TkPackage.names.find_all{|n| n =~ /^tile::theme::/}.each{|pkg| name = pkg.split('::')[-1] unless $THEMELIST.assoc(name) $THEMELIST << [name, Tk.tk_call('string', 'totitle', name)] end } # Add theme definition written by ruby $RUBY_THEMELIST = [] begin load(File.join(demodir, 'themes', 'kroc.rb'), true) rescue $RUBY_THEMELIST << ['kroc-rb', 'Kroc (by Ruby)', false] else $RUBY_THEMELIST << ['kroc-rb', 'Kroc (by Ruby)', true] end def makeThemeControl(parent) c = Tk::Tile::Labelframe.new(parent, :text=>'Theme') $THEMELIST.each{|theme, name| b = Tk::Tile::Radiobutton.new(c, :text=>name, :value=>theme, :variable=>$V.ref(:THEME), :command=>proc{setTheme(theme)}) b.grid(:sticky=>:ew) unless (TkPackage.names.find{|n| n == "tile::theme::#{theme}"}) b.state(:disabled) end } $RUBY_THEMELIST.each{|theme, name, available| b = Tk::Tile::Radiobutton.new(c, :text=>name, :value=>theme, :variable=>$V.ref(:THEME), :command=>proc{setTheme(theme)}) b.grid(:sticky=>:ew) b.state(:disabled) unless available } c end def makeThemeMenu(parent) m = TkMenu.new(parent) $THEMELIST.each{|theme, name| m.add(:radiobutton, :label=>name, :variable=>$V.ref(:THEME), :value=>theme, :command=>proc{setTheme(theme)}) unless (TkPackage.names.find{|n| n == "tile::theme::#{theme}"}) m.entryconfigure(:end, :state=>:disabled) end } $RUBY_THEMELIST.each{|theme, name, available| m.add(:radiobutton, :label=>name, :variable=>$V.ref(:THEME), :value=>theme, :command=>proc{setTheme(theme)}) m.entryconfigure(:end, :state=>:disabled) unless available } m end def setTheme(theme) if (TkPackage.names.find{|n| n == "tile::theme::#{theme}"}) TkPackage.require("tile::theme::#{theme}") end Tk::Tile::Style.theme_use(theme) end # # Load icons... # $BUTTONS = ['open', 'new', 'save'] $CHECKBOXES = ['bold', 'italic'] $ICON = {} def loadIcons(file) Tk.load_tclscript(file) img_data = TkVarAccess.new('ImgData') img_data.keys.each{|icon| $ICON[icon] = TkPhotoImage.new(:data=>img_data[icon]) } end loadIcons(File.join(demodir, 'iconlib.tcl')) # # Utilities: # def foreachWidget(wins, cmd) wins.each{|w| cmd.call(w) foreachWidget(w.winfo_children, cmd) } end # sbstub # Used as the :command option for a scrollbar, # updates the scrollbar's position. # def sbstub(sb, cmd, num, units = 'units') num = TkComm.number(num) case cmd.to_s when 'moveto' sb.set(num, num+0.5) when 'scroll' if units.to_s == 'pages' delta = 0.2 else delta = 0.05 end current = sb.get sb.set(current[0] + delta * num, current[1] + delta * num) end end # ... for debugging: TkBindTag::ALL.bind('ButtonPress-3', proc{|w| $W = w}, '%W') TkBindTag::ALL.bind('Control-ButtonPress-3', proc{|w| w.set_focus}, '%W') def showHelp() Tk.messageBox(:message=>'No help yet...') end # # See toolbutton.tcl. TkOption.add('*Toolbar.relief', :groove) TkOption.add('*Toolbar.borderWidth', 2) TkOption.add('*Toolbar.Button.Pad', 2) $ROOT = Tk.root $BASE = $ROOT Tk.destroy(*($ROOT.winfo_children)) $TOOLBARS = [] # # Toolbar button standard vs. tile comparison: # def makeToolbars # # Tile toolbar: # tb = Tk::Tile::Frame.new($BASE, :class=>'Toolbar') $TOOLBARS << tb i = 0 $BUTTONS.each{|icon| i += 1 Tk::Tile::Button.new(tb, :text=>icon, :image=>$ICON[icon], :compound=>$V[:COMPOUND], :style=>:Toolbutton).grid(:row=>0, :column=>i, :sticky=>:news) } $CHECKBOXES.each{|icon| i += 1 Tk::Tile::Checkbutton.new(tb, :text=>icon, :image=>$ICON[icon], :variable=>$V.ref(icon), :compound=>$V[:COMPOUND], :style=>:Toolbutton).grid(:row=>0, :column=>i, :sticky=>:news) } mb = Tk::Tile::Menubutton.new(tb, :text=>'toolbar', :image=>$ICON['file'], :compound=>$V[:COMPOUND]) mb.configure(:menu=>makeCompoundMenu(mb)) i += 1 mb.grid(:row=>0, :column=>i, :sticky=>:news) i += 1 tb.grid_columnconfigure(i, :weight=>1) # # Standard toolbar: # tb = TkFrame.new($BASE, :class=>'Toolbar') $TOOLBARS << tb i = 0 $BUTTONS.each{|icon| i += 1 TkButton.new(tb, :text=>icon, :image=>$ICON[icon], :compound=>$V[:COMPOUND], :relief=>:flat, :overrelief=>:raised).grid(:row=>0, :column=>i, :sticky=>:news) } $CHECKBOXES.each{|icon| i += 1 TkCheckbutton.new(tb, :text=>icon, :image=>$ICON[icon], :variable=>$V.ref(icon), :compound=>$V[:COMPOUND], :indicatoron=>false, :selectcolor=>'', :relief=>:flat, :overrelief=>:raised).grid(:row=>0, :column=>i, :sticky=>:news) } mb = TkMenubutton.new(tb, :text=>'toolbar', :image=>$ICON['file'], :compound=>$V[:COMPOUND]) mb.configure(:menu=>makeCompoundMenu(mb)) i += 1 mb.grid(:row=>0, :column=>i, :sticky=>:news) i += 1 tb.grid_columnconfigure(i, :weight=>1) end # # Toolbar :compound control: # def makeCompoundMenu(mb) menu = TkMenu.new(mb) %w(text image none top bottom left right center).each{|str| menu.add(:radiobutton, :label=>Tk.tk_call('string', 'totitle', str), :variable=>$V.ref(:COMPOUND), :value=>str, :command=>proc{ changeToolbars() }) } menu end makeToolbars() ## CONTROLS control = Tk::Tile::Frame.new($BASE) # # Overall theme control: # makeThemeControl(control).grid(:sticky=>:news, :padx=>6, :ipadx=>6) control.grid_rowconfigure(99, :weight=>1) def changeToolbars foreachWidget($TOOLBARS, proc{|w| begin w.compound($V[:COMPOUND]) rescue end }) end def scrolledWidget(parent, klass, themed, *args) if themed f = Tk::Tile::Frame.new(parent) t = klass.new(f, *args) vs = Tk::Tile::Scrollbar.new(f) hs = Tk::Tile::Scrollbar.new(f) else f = TkFrame.new(parent) t = klass.new(f, *args) vs = TkScrollbar.new(f) hs = TkScrollbar.new(f) end t.yscrollbar(vs) t.xscrollbar(hs) TkGrid.configure(t, vs, :sticky=>:news) TkGrid.configure(hs, 'x', :sticky=>:news) TkGrid.rowconfigure(f, 0, :weight=>1) TkGrid.columnconfigure(f, 0, :weight=>1) [f, t] end # # Notebook demonstration: # def makeNotebook nb = Tk::Tile::Notebook.new($BASE, :padding=>6) nb.enable_traversal client = Tk::Tile::Frame.new(nb) nb.add(client, :text=>'Demo', :underline=>0) nb.select(client) scales = Tk::Tile::Frame.new(nb) nb.add(scales, :text=>'Scales') combo = Tk::Tile::Frame.new(nb) nb.add(combo, :text=>'Combobox', :underline=>7) tree = Tk::Tile::Frame.new(nb) nb.add(tree, :text=>'Tree') others = Tk::Tile::Frame.new(nb) nb.add(others, :text=>'Others', :underline=>4) [nb, client, scales, combo, tree, others] end nb, client, scales, combo, tree, others = makeNotebook() # # Side-by side check, radio, and menu button comparison: # def fillMenu(menu) %w(above below left right flush).each{|dir| menu.add(:command, :label=>Tk.tk_call('string', 'totitle', dir), :command=>proc{ menu.winfo_parent.direction(dir) }) } menu.add(:cascade, :label=>'Submenu', :menu=>(submenu = TkMenu.new(menu))) submenu.add(:command, :label=>'Subcommand 1') submenu.add(:command, :label=>'Subcommand 2') submenu.add(:command, :label=>'Subcommand 3') menu.add(:separator) menu.add(:command, :label=>'Quit', :command=>proc{Tk.root.destroy}) end l = Tk::Tile::Labelframe.new(client, :text=>'Themed', :padding=>6) r = TkLabelframe.new(client, :text=>'Standard', :padx=>6, :pady=>6) ## Styled frame cb = Tk::Tile::Checkbutton.new(l, :text=>'Checkbutton', :variable=>$V.ref(:SELECTED), :underline=>2) rb1 = Tk::Tile::Radiobutton.new(l, :text=>'One', :variable=>$V.ref(:CHOICE), :value=>1, :underline=>0) rb2 = Tk::Tile::Radiobutton.new(l, :text=>'Two', :variable=>$V.ref(:CHOICE), :value=>2) rb3 = Tk::Tile::Radiobutton.new(l, :text=>'Three', :variable=>$V.ref(:CHOICE), :value=>3, :underline=>0) btn = Tk::Tile::Button.new(l, :text=>'Button', :underline=>0) mb = Tk::Tile::Menubutton.new(l, :text=>'Menubutton', :underline=>2) #m = TkMenu.new(mb) #mb.menu(m) #fillMenu(m) $entryText = TkVariable.new('Entry widget') e = Tk::Tile::Entry.new(l, :textvariable=>$entryText) e.selection_range(6, :end) ltext_f, ltext = scrolledWidget(l, TkText, true, :width=>12, :height=>5, :wrap=>:none) # NOTE TO MAINTAINERS: # The checkbuttons are -sticky ew / -expand x on purpose: # it demonstrates one of the differences between TCheckbuttons # and standard checkbuttons. # Tk.grid(cb, :sticky=>:ew) Tk.grid(rb1, :sticky=>:ew) Tk.grid(rb2, :sticky=>:ew) Tk.grid(rb3, :sticky=>:ew) Tk.grid(btn, :sticky=>:ew, :padx=>2, :pady=>2) Tk.grid(mb, :sticky=>:ew, :padx=>2, :pady=>2) Tk.grid(e, :sticky=>:ew, :padx=>2, :pady=>2) Tk.grid(ltext_f, :sticky=>:news) TkGrid.columnconfigure(l, 0, :weight=>1) TkGrid.rowconfigure(l, 7, :weight=>1) # text widget (grid is a PITA) ## Orig frame cb = TkCheckbutton.new(r, :text=>'Checkbutton', :variable=>$V.ref(:SELECTED)) rb1 = TkRadiobutton.new(r, :text=>'One', :variable=>$V.ref(:CHOICE), :value=>1) rb2 = TkRadiobutton.new(r, :text=>'Two', :variable=>$V.ref(:CHOICE), :value=>2, :underline=>1) rb3 = TkRadiobutton.new(r, :text=>'Three', :variable=>$V.ref(:CHOICE), :value=>3) btn = TkButton.new(r, :text=>'Button') mb = TkMenubutton.new(r, :text=>'Menubutton', :underline=>3, :takefocus=>true) m = TkMenu.new(mb) mb.menu(m) $V[:rmbIndicatoron] = mb.indicatoron m.add(:checkbutton, :label=>'Indicator?', #' :variable=>$V.ref(:rmbIndicatoron), :command=>proc{mb.indicatoron($V[:rmbIndicatoron])}) m.add(:separator) fillMenu(m) e = TkEntry.new(r, :textvariable=>$entryText) rtext_f, rtext = scrolledWidget(r, TkText, false, :width=>12, :height=>5, :wrap=>:none) Tk.grid(cb, :sticky=>:ew) Tk.grid(rb1, :sticky=>:ew) Tk.grid(rb2, :sticky=>:ew) Tk.grid(rb3, :sticky=>:ew) Tk.grid(btn, :sticky=>:ew, :padx=>2, :pady=>2) Tk.grid(mb, :sticky=>:ew, :padx=>2, :pady=>2) Tk.grid(e, :sticky=>:ew, :padx=>2, :pady=>2) Tk.grid(rtext_f, :sticky=>:news) TkGrid.columnconfigure(l, 0, :weight=>1) TkGrid.rowconfigure(l, 7, :weight=>1) # text widget (grid is a PITA) Tk.grid(l, r, :sticky=>:news, :padx=>6, :pady=>6) TkGrid.rowconfigure(client, 0, :weight=>1) TkGrid.columnconfigure(client, [0, 1], :weight=>1) # # Add some text to the text boxes: # msgs = [ "The cat crept into the crypt, crapped and crept out again", "Peter Piper picked a peck of pickled peppers", "How much wood would a woodchuck chuck if a woodchuck could chuck wood", "He thrusts his fists against the posts and still insists he sees the ghosts", "Who put the bomb in the bom-b-bom-b-bom,", "Is this your sister's sixth zither, sir?", "Who put the ram in the ramalamadingdong?", "I am not the pheasant plucker, I'm the pheasant plucker's mate." ] nmsgs = msgs.size (0...50).each{|n| msg = msgs[n % nmsgs] ltext.insert(:end, "#{n}: #{msg}\n") rtext.insert(:end, "#{n}: #{msg}\n") } # # Scales and sliders pane: # l = Tk::Tile::Labelframe.new(scales, :text=>'Themed', :padding=>6) r = TkLabelframe.new(scales, :text=>'Standard', :padx=>6, :pady=>6) if version?('0.6') # thremed frame scale = Tk::Tile::Scale.new(l, :orient=>:horizontal, :from=>0, :to=>100, :variable=>$V.ref(:SCALE)) vscale = Tk::Tile::Scale.new(l, :orient=>:vertical, :from=>0, :to=>100, :variable=>$V.ref(:VSCALE)) progress = Tk::Tile::Progressbar.new(l, :orient=>:horizontal, :maximum=>100) vprogress = Tk::Tile::Progressbar.new(l, :orient=>:vertical, :maximum=>100) if true def progress.inverted(w, value) w.value(w.maximum - value) end scale.command {|value| progress.value(value)} vscale.command {|value| progress.inverted(vprogress, value) } else # This would also work, but the Tk scale widgets # in the right hand pane cause some interference when # in autoincrement/indeterminate mode. # progress.variable $V.ref(:SCALE) vprogress.variable $V.ref(:VSCALE) end scale.set(50) vscale.set(50) lmode = Tk::Tile::Label.new(l, :text=>'Progress bar mode') pbmode0 = Tk::Tile::Radiobutton.new(l, :variable=>$V.ref(:PBMODE), :text=>'determinate', :value=>'determinate', :command=>proc{pbMode(progress, vprogress)}) pbmode1 = Tk::Tile::Radiobutton.new(l, :variable=>$V.ref(:PBMODE), :text=>'indeterminate', :value=>'indeterminate', :command=>proc{pbMode(progress, vprogress)}) def pbMode(progress, vprogress) progress.mode $V[:PBMODE] vprogress.mode $V[:PBMODE] end start = Tk::Tile::Button.new(l, :text=>"Start", :command=>proc{pbStart(progress, vprogress)}) def pbStart(progress, vprogress) $V[:PBMODE] = 'indeterminate'; pbMode(progress, vprogress) progress.start 10 vprogress.start end stop = Tk::Tile::Button.new(l, :text=>'Stop', :command=>proc{pbStop(progress, vprogress)}) def pbStop(progress, vprogress) progress.stop vprogress.stop end Tk.grid(scale, :columnspan=>2, :sticky=>'ew') Tk.grid(progress, :columnspan=>2, :sticky=>'ew') Tk.grid(vscale, vprogress, :sticky=>'nws') Tk.grid(lmode, :sticky=>'we', :columnspan=>2) Tk.grid(pbmode0, :sticky=>'we', :columnspan=>2) Tk.grid(pbmode1, :sticky=>'we', :columnspan=>2) Tk.grid(start, :sticky=>'we', :columnspan=>2) Tk.grid(stop, :sticky=>'we', :columnspan=>2) l.grid_columnconfigure(0, :weight=>1) l.grid_columnconfigure(1, :weight=>1) l.grid_rowconfigure(99, :weight=>1) # standard frame TkScale.new(r, :orient=>:horizontal, :from=>0, :to=>100, :variable=>$V.ref(:SCALE)).grid(:sticky=>'news') TkScale.new(r, :orient=>:vertical, :from=>0, :to=>100, :variable=>$V.ref(:VSCALE)).grid(:sticky=>'nws') r.grid_columnconfigure(0, :weight=>1) r.grid_columnconfigure(1, :weight=>1) r.grid_rowconfigure(99, :weight=>1) else # tile 0.5 or earlier # themed frame scale = Tk::Tile::Scale.new(l, :variable=>$V.ref(:SCALE), :orient=>:horizontal, :from=>0, :to=>100) vscale = Tk::Tile::Scale.new(l, :variable=>$V.ref(:VSCALE), :orient=>:vertical, :from=>-25, :to=>25) progress = Tk::Tile::Progress.new(l, :orient=>:horizontal, :from=>0, :to=>100) vprogress = Tk::Tile::Progress.new(l, :orient=>:vertical, :from=>-25, :to=>25) if true scale.command{|value| progress.set(value)} vscale.command{|value| vprogress.set(value)} else # this would also work. (via TkVariable#trace) v1 = $V.ref(:SCALE) v2 = $V.ref(:VSCALE) v1.trace('w', proc{ progress.set(v1.value) }) v2.trace('w', proc{ vprogress.set(v2.value) }) end Tk.grid(scale, :columnspan=>2, :sticky=>:ew) Tk.grid(progress, :columnspan=>2, :sticky=>:ew) Tk.grid(vscale, vprogress, :sticky=>:nws) TkGrid.columnconfigure(l, 0, :weight=>1) TkGrid.columnconfigure(l, 1, :weight=>1) # standard frame TkScale.new(r, :variable=>$V.ref(:SCALE), :orient=>:horizontal, :from=>0, :to=>100).grid(:sticky=>'news') TkScale.new(r, :variable=>$V.ref(:VSCALE), :orient=>:vertical, :from=>-25, :to=>25).grid(:sticky=>'nws') TkGrid.columnconfigure(r, 0, :weight=>1) TkGrid.columnconfigure(r, 1, :weight=>1) end # layout frames Tk.grid(l, r, :sticky=>'nwes', :padx=>6, :pady=>6) scales.grid_columnconfigure(0, :weight=>1) scales.grid_columnconfigure(1, :weight=>1) scales.grid_rowconfigure(0, :weight=>1) # # Command box: # cmd = Tk::Tile::Frame.new($BASE) b_close = Tk::Tile::Button.new(cmd, :text=>'Close', :underline=>0, :default=>:normal, :command=>proc{Tk.root.destroy}) b_help = Tk::Tile::Button.new(cmd, :text=>'Help', :underline=>0, :default=>:normal, :command=>proc{showHelp()}) Tk.grid('x', b_close, b_help, :pady=>[6, 4], :padx=>4) TkGrid.columnconfigure(cmd, 0, :weight=>1) # # Set up accelerators: # $ROOT.bind('KeyPress-Escape', proc{Tk.event_generate(b_close, '')}) $ROOT.bind('', proc{Tk.event_generate(b_help, '')}) Tk::Tile::KeyNav.enableMnemonics($ROOT) Tk::Tile::KeyNav.defaultButton(b_help) Tk.grid($TOOLBARS[0], '-', :sticky=>:ew) Tk.grid($TOOLBARS[1], '-', :sticky=>:ew) Tk.grid(control, nb, :sticky=>:news) Tk.grid(cmd, '-', :sticky=>:ew) TkGrid.columnconfigure($ROOT, 1, :weight=>1) TkGrid.rowconfigure($ROOT, 2, :weight=>1) # # Add a menu # menu = TkMenu.new($BASE) $ROOT.menu(menu) m_file = TkMenu.new(menu, :tearoff=>0) menu.add(:cascade, :label=>'File', :underline=>0, :menu=>m_file) m_file.add(:command, :label=>'Open', :underline=>0, :compound=>:left, :image=>$ICON['open']) m_file.add(:command, :label=>'Save', :underline=>0, :compound=>:left, :image=>$ICON['save']) m_file.add(:separator) m_f_test = TkMenu.new(menu, :tearoff=>0) m_file.add(:cascade, :label=>'Test submenu', :underline=>0, :menu=>m_f_test) m_file.add(:checkbutton, :label=>'Text check', :underline=>5, :variable=>$V.ref(:MENUCHECK1)) m_file.insert(:end, :separator) if Tk.windowingsystem != 'x11' TkConsole.create m_file.insert(:end, :checkbutton, :label=>'Console', :underline=>5, :variable=>$V.ref(:CONSOLE), :command=>proc{toggle_console()}) def toggle_console if TkComm.bool($V[:CONSOLE]) TkConsole.show else TkConsole.hide end end end m_file.add(:command, :label=>'Exit', :underline=>1, :command=>proc{Tk.event_generate(b_close, '')}) %w(One Two Three Four).each{|lbl| m_f_test.add(:radiobutton, :label=>lbl, :variable=>$V.ref(:MENURADIO1)) } # Add Theme menu. # menu.add(:cascade, :label=>'Theme', :underline=>3, :menu=>makeThemeMenu(menu)) setTheme($V[:THEME]) # # Combobox demo pane: # values = %w(list abc def ghi jkl mno pqr stu vwx yz) 2.times {|i| cb = Tk::Tile::Combobox.new( combo, :values=>values, :textvariable=>$V.ref(:COMBO)) cb.pack(:side=>:top, :padx=>2, :pady=>2, :expand=>false, :fill=>:x) if i == 1 cb.state :readonly begin cb.current = 3 # ignore if unsupported (tile0.4) rescue end end } # # Treeview widget demo pane: # if version?('0.5') treeview = nil # avoid 'undefined' error scrollbar = Tk::Tile::Scrollbar.new(tree, :command=>proc{|*args| treeview.yview(*args)}) treeview = Tk::Tile::Treeview.new(tree, :columns=>%w(Class), :padding=>4, :yscrollcommand=>proc{|*args| scrollbar.set(*args)}) Tk.grid(treeview, scrollbar, :sticky=>'news') tree.grid_columnconfigure(0, :weight=>1) tree.grid_rowconfigure(0, :weight=>1) tree.grid_propagate(0) # Add initial tree node: # Later nodes will be added in <> binding. treeview.insert('', 0, :id=>'.', :text=>'Main Window', :open=>false, :values=>[TkWinfo.classname('.')]) treeview.headingconfigure('#0', :text=>'Widget') treeview.headingconfigure('Class', :text=>'Class') treeview.bind('', proc{fillTree(treeview)}) def fillTree(treeview) id = treeview.focus_item unless TkWinfo.exist?(id) treeview.delete(id) end # Replace tree item children with current list of child windows. treeview.delete(treeview.children(id)) for child in TkWinfo.children(id) treeview.insert(id, :end, :id=>child, :text=>TkWinfo.appname(child), :open=>false, :values=>[TkWinfo.classname(child)]) unless TkWinfo.children(child).empty? # insert dummy child to show [+] indicator treeview.insert(child, :end) end end end else Tk::Tile::Label.new(tree, :text=>'Treeview is supported on tile 0.5 or later...').pack end # # Other demos: # $Timers = {:StateMonitor=>nil, :FocusMonitor=>nil} msg = TkMessage.new(others, :aspect=>200) $Desc = {} showDescription = TkBindTag.new showDescription.bind('Enter', proc{|w| msg.text($Desc[w.path])}, '%W') showDescription.bind('Leave', proc{|w| msg.text('')}, '%W') [ [ :trackStates, "Widget states...", "Display/modify widget state bits" ], [ :scrollbarResizeDemo, "Scrollbar resize behavior...", "Shows how Tile and standard scrollbars differ when they're sized too large" ], [ :trackFocus, "Track keyboard focus..." , "Display the name of the widget that currently has focus" ], [ :repeatDemo, "Repeating buttons...", "Demonstrates custom classes (see demos/repeater.tcl)" ] ].each{|demo_cmd, label, description| b = Tk::Tile::Button.new(others, :text=>label, :command=>proc{ self.__send__(demo_cmd) }) $Desc[b.path] = description b.bindtags <<= showDescription b.pack(:side=>:top, :expand=>false, :fill=>:x, :padx=>6, :pady=>6) } msg.pack(:side=>:bottom, :expand=>true, :fill=>:both) # # Scrollbar resize demo: # $scrollbars = nil def scrollbarResizeDemo if $scrollbars begin $scrollbars.destroy rescue end end $scrollbars = TkToplevel.new(:title=>'Scrollbars', :geometry=>'200x200') f = TkFrame.new($scrollbars, :height=>200) tsb = Tk::Tile::Scrollbar.new(f, :command=>proc{|*args| sbstub(tsb, *args)}) sb = TkScrollbar.new(f, :command=>proc{|*args| sbstub(sb, *args)}) Tk.grid(tsb, sb, :sticky=>:news) sb.set(0, 0.5) # prevent backwards-compatibility mode for old SB f.grid_columnconfigure(0, :weight=>1) f.grid_columnconfigure(1, :weight=>1) f.grid_rowconfigure(0, :weight=>1) f.pack(:expand=>true, :fill=>:both) end # # Track focus demo: # $FocusInf = TkVariable.new_hash $focus = nil def trackFocus if $focus begin $focus.destroy rescue end end $focus = TkToplevel.new(:title=>'Keyboard focus') i = 0 [ ["Focus widget:", :Widget], ["Class:", :WidgetClass], ["Next:", :WidgetNext], ["Grab:", :Grab], ["Status:", :GrabStatus] ].each{|label, var_index| Tk.grid(Tk::Tile::Label.new($focus, :text=>label, :anchor=>:e), Tk::Tile::Label.new($focus, :textvariable=>$FocusInf.ref(var_index), :width=>40, :anchor=>:w, :relief=>:groove), :sticky=>:ew) i += 1 } $focus.grid_columnconfigure(1, :weight=>1) $focus.grid_rowconfigure(i, :weight=>1) $focus.bind('Destroy', proc{Tk.after_cancel($Timers[:FocusMonitor])}) focusMonitor end def focusMonitor $FocusInf[:Widget] = focus_win = Tk.focus if focus_win $FocusInf[:WidgetClass] = focus_win.winfo_classname $FocusInf[:WidgetNext] = Tk.focus_next(focus_win) else $FocusInf[:WidgetClass] = $FocusInf[:WidgetNext] = '' end $FocusInf[:Grab] = grab_wins = Tk.current_grabs unless grab_wins.empty? $FocusInf[:GrabStatus] = grab_wins[0].grab_status else $FocusInf[:GrabStatus] = '' end $Timers[:FocusMonitor] = Tk.after(200, proc{ focusMonitor() }) end # # Widget state demo: # $Widget = TkVariable.new TkBindTag::ALL.bind('Control-Shift-ButtonPress-1', proc{|w| $Widget.value = w updateStates() Tk.callback_break }, '%W') $states_list = %w(active disabled focus pressed selected background indeterminate invalid default) $states_btns = {} $states = nil $State = TkVariable.new_hash def trackStates if $states begin $state.destroy rescue end end $states = TkToplevel.new(:title=>'Widget states') l_inf = Tk::Tile::Label.new($states, :text=>"Press Control-Shift-Button-1 on any widget") l_lw = Tk::Tile::Label.new($states, :text=>'Widget:', :anchor=>:e, :relief=>:groove) l_w = Tk::Tile::Label.new($states, :textvariable=>$Widget, :anchor=>:w, :relief=>:groove) Tk.grid(l_inf, '-', :sticky=>:ew, :padx=>6, :pady=>6) Tk.grid(l_lw, l_w, :sticky=>:ew) $states_list.each{|st| cb = Tk::Tile::Checkbutton.new($states, :text=>st, :variable=>$State.ref(st), :command=>proc{ changeState(st) }) $states_btns[st] = cb Tk.grid('x', cb, :sticky=>:nsew) } $states.grid_columnconfigure(1, :weight=>1) f_cmd = Tk::Tile::Frame.new($states) Tk.grid('x', f_cmd, :sticky=>:nse) b_close = Tk::Tile::Button.new(f_cmd, :text=>'Close', :command=>proc{ $states.destroy }) Tk.grid('x', b_close, :padx=>4, :pady=>[6,4]) f_cmd.grid_columnconfigure(0, :weight=>1) $states.bind('KeyPress-Escape', proc{Tk.event_generate(b_close, '')}) $states.bind('Destroy', proc{Tk.after_cancel($Timers[:StateMonitor])}) stateMonitor() end def stateMonitor updateStates() if $Widget.value != '' $Timers[:StateMonitor] = Tk.after(200, proc{ stateMonitor() }) end def updateStates $states_list.each{|st| begin $State[st] = $Widget.window.instate(st) rescue $states_btns[st].state('disabled') else $states_btns[st].state('!disabled') end } end def changeState(st) if $Widget.value != '' if $State.bool_element(st) $Widget.window.state(st) else $Widget.window.state("!#{st}") end end end # # Repeating buttons demo: # def repeatDemo if defined?($repeatDemo) && $repeatDemo.exist? $repeatDemo.deiconify; return end $repeatDemo = TkToplevel.new(:title=>'Repeating button') f = Tk::Tile::Frame.new($repeatDemo) b = Tk::Tile::Button.new(f, :class=>'Repeater', :text=>'Press and hold') if version?('0.6') p = Tk::Tile::Progressbar.new(f, :orient=>:horizontal, :maximum=>10) else # progressbar is not supported p = Tk::Tile::Progress.new(f, :orient=>:horizontal, :from=>0, :to=>10) def p.step i = self.get + 1 i = self.from if i > self.to self.set(i) end end b.command {p.step} b.pack(:side=>:left, :expand=>false, :fill=>:none, :padx=>6, :pady=>6) p.pack(:side=>:right, :expand=>true, :fill=>:x, :padx=>6, :pady=>6) f.pack(:expand=>true, :fill=>:both) end Tk.mainloop