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