1# 2# tkballoonhelp.rb : simple balloon help widget 3# by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp) 4# 5# Add a balloon help to a widget. 6# This widget has only poor featureas. If you need more useful features, 7# please try to use the Tix extension of Tcl/Tk under Ruby/Tk. 8# 9# The interval time to display a balloon help is defined 'interval' option 10# (default is 750ms). 11# 12require 'tk' 13 14module Tk 15 module RbWidget 16 class BalloonHelp<TkLabel 17 end 18 end 19end 20class Tk::RbWidget::BalloonHelp<TkLabel 21 DEFAULT_FOREGROUND = 'black' 22 DEFAULT_BACKGROUND = 'white' 23 DEFAULT_INTERVAL = 750 24 25 def _balloon_binding(interval) 26 @timer = TkAfter.new(interval, 1, proc{show}) 27 def @timer.interval(val) 28 @sleep_time = val 29 end 30 @bindtag = TkBindTag.new 31 @bindtag.bind('Enter', proc{@timer.start}) 32 @bindtag.bind('Motion', proc{@timer.restart; erase}) 33 @bindtag.bind('Any-ButtonPress', proc{@timer.restart; erase}) 34 @bindtag.bind('Leave', proc{@timer.stop; erase}) 35 tags = @parent.bindtags 36 idx = tags.index(@parent) 37 unless idx 38 ppath = TkComm.window(@parent.path) 39 idx = tags.index(ppath) || 0 40 end 41 tags[idx,0] = @bindtag 42 @parent.bindtags(tags) 43 end 44 private :_balloon_binding 45 46 def initialize(parent=nil, keys={}) 47 @parent = parent || Tk.root 48 49 @frame = TkToplevel.new(@parent) 50 @frame.withdraw 51 @frame.overrideredirect(true) 52 @frame.transient(TkWinfo.toplevel(@parent)) 53 @epath = @frame.path 54 55 if keys 56 keys = _symbolkey2str(keys) 57 else 58 keys = {} 59 end 60 61 @command = keys.delete('command') 62 63 @interval = keys.delete('interval'){DEFAULT_INTERVAL} 64 _balloon_binding(@interval) 65 66 # @label = TkLabel.new(@frame, 'background'=>'bisque').pack 67 @label = TkLabel.new(@frame, 68 'foreground'=>DEFAULT_FOREGROUND, 69 'background'=>DEFAULT_BACKGROUND).pack 70 @label.configure(_symbolkey2str(keys)) unless keys.empty? 71 @path = @label 72 end 73 74 def epath 75 @epath 76 end 77 78 def interval(val) 79 if val 80 @timer.interval(val) 81 else 82 @interval 83 end 84 end 85 86 def command(cmd = Proc.new) 87 @command = cmd 88 self 89 end 90 91 def show 92 x = TkWinfo.pointerx(@parent) 93 y = TkWinfo.pointery(@parent) 94 @frame.geometry("+#{x+1}+#{y+1}") 95 96 if @command 97 case @command.arity 98 when 0 99 @command.call 100 when 2 101 @command.call(x - TkWinfo.rootx(@parent), y - TkWinfo.rooty(@parent)) 102 when 3 103 @command.call(x - TkWinfo.rootx(@parent), y - TkWinfo.rooty(@parent), 104 self) 105 else 106 @command.call(x - TkWinfo.rootx(@parent), y - TkWinfo.rooty(@parent), 107 self, @parent) 108 end 109 end 110 111 @frame.deiconify 112 @frame.raise 113 114 begin 115 @org_cursor = @parent.cget('cursor') 116 rescue 117 @org_cursor = @parent['cursor'] 118 end 119 begin 120 @parent.configure('cursor', 'crosshair') 121 rescue 122 @parent.cursor('crosshair') 123 end 124 end 125 126 def erase 127 begin 128 @parent.configure('cursor', @org_cursor) 129 rescue 130 @parent.cursor(@org_cursor) 131 end 132 @frame.withdraw 133 end 134 135 def destroy 136 @frame.destroy 137 end 138end 139 140################################################ 141# test 142################################################ 143if __FILE__ == $0 144 TkButton.new('text'=>'This button has a balloon help') {|b| 145 pack('fill'=>'x') 146 Tk::RbWidget::BalloonHelp.new(b, 'text'=>' Message ') 147 } 148 TkButton.new('text'=>'This button has another balloon help') {|b| 149 pack('fill'=>'x') 150 Tk::RbWidget::BalloonHelp.new(b, 151 'text'=>"CONFIGURED MESSAGE\nchange colors, and so on", 152 'interval'=>200, 'font'=>'courier', 153 'background'=>'gray', 'foreground'=>'red') 154 } 155 156 sb = TkScrollbox.new.pack(:fill=>:x) 157 sb.insert(:end, *%w(aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk lll mmm)) 158=begin 159 # CASE1 : command takes no arguemnt 160 bh = Tk::RbWidget::BalloonHelp.new(sb, :interval=>500, 161 :relief=>:ridge, :background=>'white', 162 :command=>proc{ 163 y = TkWinfo.pointery(sb) - TkWinfo.rooty(sb) 164 bh.text "current index == #{sb.nearest(y)}" 165 }) 166=end 167=begin 168 # CASE2 : command takes 2 arguemnts 169 bh = Tk::RbWidget::BalloonHelp.new(sb, :interval=>500, 170 :relief=>:ridge, :background=>'white', 171 :command=>proc{|x, y| 172 bh.text "current index == #{sb.nearest(y)}" 173 }) 174=end 175=begin 176 # CASE3 : command takes 3 arguemnts 177 Tk::RbWidget::BalloonHelp.new(sb, :interval=>500, 178 :relief=>:ridge, :background=>'white', 179 :command=>proc{|x, y, bhelp| 180 bhelp.text "current index == #{sb.nearest(y)}" 181 }) 182=end 183=begin 184 # CASE4a : command is a Proc object and takes 4 arguemnts 185 cmd = proc{|x, y, bhelp, parent| 186 bhelp.text "current index == #{parent.nearest(y)}" 187 } 188 189 Tk::RbWidget::BalloonHelp.new(sb, :interval=>500, 190 :relief=>:ridge, :background=>'white', 191 :command=>cmd) 192 193 sb2 = TkScrollbox.new.pack(:fill=>:x) 194 sb2.insert(:end, *%w(AAA BBB CCC DDD EEE FFF GGG HHH III JJJ KKK LLL MMM)) 195 Tk::RbWidget::BalloonHelp.new(sb2, :interval=>500, 196 :padx=>5, :relief=>:raised, 197 :background=>'gray25', :foreground=>'white', 198 :command=>cmd) 199=end 200#=begin 201 # CASE4b : command is a Method object and takes 4 arguemnts 202 def set_msg(x, y, bhelp, parent) 203 bhelp.text "current index == #{parent.nearest(y)}" 204 end 205 cmd = self.method(:set_msg) 206 207 Tk::RbWidget::BalloonHelp.new(sb, :interval=>500, 208 :relief=>:ridge, :background=>'white', 209 :command=>cmd) 210 211 sb2 = TkScrollbox.new.pack(:fill=>:x) 212 sb2.insert(:end, *%w(AAA BBB CCC DDD EEE FFF GGG HHH III JJJ KKK LLL MMM)) 213 Tk::RbWidget::BalloonHelp.new(sb2, :interval=>500, 214 :padx=>5, :relief=>:raised, 215 :background=>'gray25', :foreground=>'white', 216 :command=>cmd) 217#=end 218 219 Tk.mainloop 220end 221