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