1# 2# Tk::RbWidget::ScrollFrame class 3# 4# This widget class is a frame widget with scrollbars. 5# The ScrollFrame doesn't propagate the size of embedded widgets. 6# When it is configured, scrollregion of the container is changed. 7# 8# Scrollbars can be toggled by Tk::RbWidget::ScrollFrame#vscroll & hscroll. 9# If horizontal or virtical scrollbar is turned off, the horizontal 10# or virtical size of embedded widgets is propagated. 11# 12# Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp) 13# 14require 'tk' 15 16module Tk::RbWidget; end 17 18class Tk::RbWidget::ScrollFrame < TkFrame 19 include TkComposite 20 21 DEFAULT_WIDTH = 200 22 DEFAULT_HEIGHT = 200 23 24 def initialize_composite(keys={}) 25 @frame.configure(:width=>DEFAULT_WIDTH, :height=>DEFAULT_HEIGHT) 26 27 # create scrollbars 28 @h_scroll = TkScrollbar.new(@frame, 'orient'=>'horizontal') 29 @v_scroll = TkScrollbar.new(@frame, 'orient'=>'vertical') 30 31 # create a canvas widget 32 @canvas = TkCanvas.new(@frame, 33 :borderwidth=>0, :selectborderwidth=>0, 34 :highlightthickness=>0) 35 36 # allignment 37 TkGrid.rowconfigure(@frame, 0, 'weight'=>1, 'minsize'=>0) 38 TkGrid.columnconfigure(@frame, 0, 'weight'=>1, 'minsize'=>0) 39 @canvas.grid('row'=>0, 'column'=>0, 'sticky'=>'news') 40 @frame.grid_propagate(false) 41 42 # assign scrollbars 43 @canvas.xscrollbar(@h_scroll) 44 @canvas.yscrollbar(@v_scroll) 45 46 # convert hash keys 47 keys = _symbolkey2str(keys) 48 49 # check options for the frame 50 framekeys = {} 51 if keys.key?('classname') 52 keys['class'] = keys.delete('classname') 53 end 54 if @classname = keys.delete('class') 55 framekeys['class'] = @classname 56 end 57 if @colormap = keys.delete('colormap') 58 framekeys['colormap'] = @colormap 59 end 60 if @container = keys.delete('container') 61 framekeys['container'] = @container 62 end 63 if @visual = keys.delete('visual') 64 framekeys['visual'] = @visual 65 end 66 if @classname.kind_of? TkBindTag 67 @db_class = @classname 68 @classname = @classname.id 69 elsif @classname 70 @db_class = TkDatabaseClass.new(@classname) 71 else 72 @db_class = self.class 73 @classname = @db_class::WidgetClassName 74 end 75 76 # create base frame 77 @base = TkFrame.new(@canvas, framekeys) 78 79 # embed base frame 80 @cwin = TkcWindow.new(@canvas, [0, 0], :window=>@base, :anchor=>'nw') 81 @canvas.scrollregion(@cwin.bbox) 82 83 # binding to reset scrollregion 84 @base.bind('Configure'){ _reset_scrollregion(nil, nil) } 85 86 # set default receiver of method calls 87 @path = @base.path 88 89 # scrollbars ON 90 vscroll(keys.delete('vscroll'){true}) 91 hscroll(keys.delete('hscroll'){true}) 92 93 # please check the differences of the following definitions 94 option_methods( 95 :scrollbarwidth 96 ) 97 98 # set receiver widgets for configure methods (with alias) 99 delegate_alias('scrollbarrelief', 'relief', @h_scroll, @v_scroll) 100 101 # set receiver widgets for configure methods 102 delegate('DEFAULT', @base) 103 delegate('background', @frame, @base, @canvas, @h_scroll, @v_scroll) 104 delegate('width', @frame) 105 delegate('height', @frame) 106 delegate('activebackground', @h_scroll, @v_scroll) 107 delegate('troughcolor', @h_scroll, @v_scroll) 108 delegate('repeatdelay', @h_scroll, @v_scroll) 109 delegate('repeatinterval', @h_scroll, @v_scroll) 110 delegate('borderwidth', @frame) 111 delegate('relief', @frame) 112 113 # do configure 114 configure keys unless keys.empty? 115 end 116 117 # callback for Configure event 118 def _reset_scrollregion(h_mod=nil, v_mod=nil) 119 cx1, cy1, cx2, cy2 = @canvas.scrollregion 120 x1, y1, x2, y2 = @cwin.bbox 121 @canvas.scrollregion([x1, y1, x2, y2]) 122 123 if h_mod.nil? && v_mod.nil? 124 if x2 != cx2 && TkGrid.info(@h_scroll).size == 0 125 @frame.grid_propagate(true) 126 @canvas.width = x2 127 Tk.update_idletasks 128 @frame.grid_propagate(false) 129 end 130 if y2 != cy2 && TkGrid.info(@v_scroll).size == 0 131 @frame.grid_propagate(true) 132 @canvas.height = y2 133 Tk.update_idletasks 134 @frame.grid_propagate(false) 135 end 136 else 137 @h_scroll.ungrid if h_mod == false 138 @v_scroll.ungrid if v_mod == false 139 140 h_flag = (TkGrid.info(@h_scroll).size == 0) 141 v_flag = (TkGrid.info(@v_scroll).size == 0) 142 143 @frame.grid_propagate(true) 144 145 @canvas.width = (h_flag)? x2: @canvas.winfo_width 146 @canvas.height = (v_flag)? y2: @canvas.winfo_height 147 148 @h_scroll.grid('row'=>1, 'column'=>0, 'sticky'=>'ew') if h_mod 149 @v_scroll.grid('row'=>0, 'column'=>1, 'sticky'=>'ns') if v_mod 150 151 Tk.update_idletasks 152 153 @frame.grid_propagate(false) 154 end 155 end 156 private :_reset_scrollregion 157 158 # forbid to change binding of @base frame 159 def bind(*args) 160 @frame.bind(*args) 161 end 162 def bind_append(*args) 163 @frame.bind_append(*args) 164 end 165 def bind_remove(*args) 166 @frame.bind_remove(*args) 167 end 168 def bindinfo(*args) 169 @frame.bindinfo(*args) 170 end 171 172 # set width of scrollbar 173 def scrollbarwidth(width = nil) 174 if width 175 @h_scroll.width(width) 176 @v_scroll.width(width) 177 else 178 @h_scroll.width 179 end 180 end 181 182 # vertical scrollbar : ON/OFF 183 def vscroll(mode) 184 Tk.update_idletasks 185 st = TkGrid.info(@v_scroll) 186 if mode && st.size == 0 then 187 @v_scroll.grid('row'=>0, 'column'=>1, 'sticky'=>'ns') 188 _reset_scrollregion(nil, true) 189 elsif !mode && st.size != 0 then 190 _reset_scrollregion(nil, false) 191 else 192 _reset_scrollregion(nil, nil) 193 end 194 self 195 end 196 197 # horizontal scrollbar : ON/OFF 198 def hscroll(mode) 199 Tk.update_idletasks 200 st = TkGrid.info(@h_scroll) 201 if mode && st.size == 0 then 202 _reset_scrollregion(true, nil) 203 elsif !mode && st.size != 0 then 204 _reset_scrollregion(false, nil) 205 else 206 _reset_scrollregion(nil, nil) 207 end 208 self 209 end 210end 211 212# test 213if __FILE__ == $0 214 f = Tk::RbWidget::ScrollFrame.new(:scrollbarwidth=>10, 215 :width=>300, :height=>200) 216 f.pack(:expand=>true, :fill=>:both) 217 218 TkButton.new(f, :text=>'foo button', :command=>proc{puts 'foo'}).pack 219 TkButton.new(f, :text=>'baaar button', :command=>proc{puts 'baaar'}).pack 220 TkButton.new(f, :text=>'baz button', :command=>proc{puts 'baz'}).pack 221 TkButton.new(f, :text=>'hoge hoge button', 222 :command=>proc{puts 'hoge hoge'}).pack(:side=>:bottom) 223 224 # f.hscroll(false) 225 226 # add a text widget 227 Tk.after(3000){ 228 t = TkText.new(f).pack(:expand=>true, :fill=>:both) 229 t.insert(:end, "An example of Tk::RbWidget::ScrollFrame widget.\n\n") 230 t.insert(:end, "Here is a text widget.\n") 231 t.insert(:end, "Please resize the application window, ") 232 t.insert(:end, "and try the scrollbars ") 233 t.insert(:end, "to move the view of packed widgets.\n") 234 } 235 236 # remove a vertical scrollbar, and then the scrollframe is not scrollable. 237 Tk.after(6000){ f.vscroll(false) } 238 239 # add a vertical scrollbar, and make the scrollframe scrollable. 240 Tk.after(9000){ f.vscroll(true) } 241 242 # remove a horizontal scrollbar, and then the scrollframe is not scrollable. 243 Tk.after(12000){ f.hscroll(false) } 244 245 # add a horizontal scrollbar, and make the scrollframe scrollable. 246 Tk.after(15000){ f.hscroll(true) } 247 248 Tk.mainloop 249end 250