1#
2#  tkalignbox.rb : align widgets with same width/height
3#
4#                                            by Hidetoshi NAGAI
5#
6#  The box size depends on 'reqheight' and 'reqwidth' of contained widgets.
7#  If you want to give the box size when those requested sizes are 0,
8#  please set box.propagate = false (See the test routine at the tail of
9#  this file).
10
11require 'tk'
12
13module Tk
14  module RbWidget
15    class AlignBox < TkFrame
16    end
17  end
18end
19
20class Tk::RbWidget::AlignBox < TkFrame
21  def initialize(*args)
22    if self.class == Tk::RbWidget::AlignBox
23      fail RuntimeError, "Tk::AlignBox is an abstract class"
24    end
25    @padx = 0
26    @pady = 0
27    if args[-1].kind_of? Hash
28      keys = _symbolkey2str(args.pop)
29      @padx = keys.delete('padx') || 0
30      @pady = keys.delete('pady') || 0
31      args.push(keys)
32    end
33    super(*args)
34    @max_width = 0
35    @max_height = 0
36    @propagate = true
37    @widgets = []
38  end
39
40  def _set_framesize
41    fail RuntimeError, "Tk::AlignBox is an abstract class"
42  end
43  private :_set_framesize
44
45  def _place_config(widget, idx, cnt)
46    fail RuntimeError, "Tk::AlignBox is an abstract class"
47  end
48  private :_place_config
49
50  def align
51    widgets = []
52    @widgets.each{|w| widgets << w if w.winfo_exist?}
53    @widgets = widgets
54    cnt = @widgets.size.to_f
55    @widgets.each_with_index{|w, idx| _place_config(w, idx, cnt)}
56    @widgets = widgets
57    _set_framesize if @propagate
58    self
59  end
60
61  def add(*widgets)
62    widgets.each{|w|
63      unless w.kind_of? TkWindow
64        fail RuntimeError, "#{w.inspect} is not a widget instance."
65      end
66      @widgets.delete(w)
67      @widgets << w
68      sz = w.winfo_reqwidth
69      @max_width = sz if @max_width < sz
70      sz = w.winfo_reqheight
71      @max_height = sz if @max_height < sz
72    }
73    align
74  end
75
76  def <<(widget)
77    add(widget)
78  end
79
80  def insert(idx, widget)
81    unless widget.kind_of? TkWindow
82      fail RuntimeError, "#{widget.inspect} is not a widget instance."
83    end
84    @widgets.delete(widget)
85    @widgets[idx,0] = widget
86    sz = widget.winfo_reqwidth
87    @max_width = sz if @max_width < sz
88    sz = widget.winfo_reqheight
89    @max_height = sz if @max_height < sz
90    align
91  end
92
93  def delete(idx)
94    ret = @widgets.delete_at(idx)
95    @req_size = 0
96    @widget.each{|w|
97      sz = w.winfo_reqwidth
98      @max_width = sz if @max_width < sz
99      sz = w.winfo_reqheight
100      @max_height = sz if @max_height < sz
101    }
102    align
103    ret
104  end
105
106  def padx(size = nil)
107    if size
108      @padx = size
109      align
110    else
111      @padx
112    end
113  end
114
115  def pady(size = nil)
116    if size
117      @pady = size
118      align
119    else
120      @pady
121    end
122  end
123
124  attr_accessor :propagate
125end
126
127class Tk::RbWidget::HBox < Tk::RbWidget::AlignBox
128  def _set_framesize
129    bd = self.borderwidth
130    self.width((@max_width + 2*@padx) * @widgets.size + 2*bd)
131    self.height(@max_height + 2*@pady + 2*bd)
132  end
133  private :_set_framesize
134
135  def _place_config(widget, idx, cnt)
136    widget.place_in(self,
137                    'relx'=>idx/cnt, 'x'=>@padx,
138                    'rely'=>0, 'y'=>@pady,
139                    'relwidth'=>1.0/cnt, 'width'=>-2*@padx,
140                    'relheight'=>1.0, 'height'=>-2*@pady)
141  end
142  private :_place_config
143end
144Tk::RbWidget::HLBox = Tk::RbWidget::HBox
145
146class Tk::RbWidget::HRBox < Tk::RbWidget::HBox
147  def _place_config(widget, idx, cnt)
148    widget.place_in(self,
149                    'relx'=>(cnt - idx - 1)/cnt, 'x'=>@padx,
150                    'rely'=>0, 'y'=>@pady,
151                    'relwidth'=>1.0/cnt, 'width'=>-2*@padx,
152                    'relheight'=>1.0, 'height'=>-2*@pady)
153  end
154  private :_place_config
155end
156
157class Tk::RbWidget::VBox < Tk::RbWidget::AlignBox
158  def _set_framesize
159    bd = self.borderwidth
160    self.width(@max_width + 2*@padx + 2*bd)
161    self.height((@max_height + 2*@pady) * @widgets.size + 2*bd)
162  end
163  private :_set_framesize
164
165  def _place_config(widget, idx, cnt)
166    widget.place_in(self,
167                    'relx'=>0, 'x'=>@padx,
168                    'rely'=>idx/cnt, 'y'=>@pady,
169                    'relwidth'=>1.0, 'width'=>-2*@padx,
170                    'relheight'=>1.0/cnt, 'height'=>-2*@pady)
171  end
172  private :_place_config
173end
174Tk::RbWidget::VTBox = Tk::RbWidget::VBox
175
176class Tk::RbWidget::VBBox < Tk::RbWidget::VBox
177  def _place_config(widget, idx, cnt)
178    widget.place_in(self,
179                    'relx'=>0, 'x'=>@padx,
180                    'rely'=>(cnt - idx - 1)/cnt, 'y'=>@pady,
181                    'relwidth'=>1.0, 'width'=>-2*@padx,
182                    'relheight'=>1.0/cnt, 'height'=>-2*@pady)
183  end
184  private :_place_config
185end
186
187################################################
188# test
189################################################
190if __FILE__ == $0
191  f = Tk::RbWidget::HBox.new(:borderwidth=>3, :relief=>'ridge').pack
192  f.add(TkButton.new(f, :text=>'a'),
193        TkButton.new(f, :text=>'aa', :font=>'Helvetica 16'),
194        TkButton.new(f, :text=>'aaa'),
195        TkButton.new(f, :text=>'aaaa'))
196
197  f = Tk::RbWidget::HBox.new(:borderwidth=>3, :relief=>'ridge',
198                             :padx=>7, :pady=>3, :background=>'yellow').pack
199  f.add(TkButton.new(f, :text=>'a'),
200        TkButton.new(f, :text=>'aa', :font=>'Helvetica 16'),
201        TkButton.new(f, :text=>'aaa'),
202        TkButton.new(f, :text=>'aaaa'))
203
204  f = Tk::RbWidget::VBox.new(:borderwidth=>5,
205                             :relief=>'groove').pack(:fill=>:y, :expand=>true)
206  f.add(TkButton.new(f, :text=>'a'),
207        TkButton.new(f, :text=>'aa', :font=>'Helvetica 30'),
208        TkButton.new(f, :text=>'aaa'),
209        TkButton.new(f, :text=>'aaaa'))
210
211  f = Tk::RbWidget::HRBox.new(:borderwidth=>3,
212                              :relief=>'raised').pack(:fill=>:x)
213  f.add(TkButton.new(f, :text=>'a'),
214        TkButton.new(f, :text=>'aa'),
215        TkButton.new(f, :text=>'aaa'))
216
217  f = Tk::RbWidget::VBBox.new(:borderwidth=>3,
218                              :relief=>'ridge').pack(:fill=>:x)
219  f.propagate = false
220  f.height 100
221  f.add(TkFrame.new(f){|ff|
222          TkButton.new(ff, :text=>'a').pack(:pady=>4, :padx=>6,
223                                            :fill=>:both, :expand=>true)
224        },
225        TkFrame.new(f){|ff|
226          TkButton.new(ff, :text=>'aa').pack(:pady=>4, :padx=>6,
227                                             :fill=>:both, :expand=>true)
228        },
229        TkFrame.new(f){|ff|
230          TkButton.new(ff, :text=>'aaaa').pack(:pady=>4, :padx=>6,
231                                               :fill=>:both, :expand=>true)
232        })
233
234  Tk.mainloop
235end
236