2
# tkcombobox.rb : TkAutoScrollbox & TkCombobox
4
# by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
8
class TkAutoScrollbox < TkListbox
11
@@up_bmp = TkBitmapImage.new(:data=><<EOD)
12
#define up_arrow_width 9
13
#define up_arrow_height 9
14
static unsigned char up_arrow_bits[] = {
15
0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x38, 0x00, 0x38, 0x00, 0x7c, 0x00,
16
0x7c, 0x00, 0xfe, 0x00, 0x00, 0x00};
19
@@down_bmp = TkBitmapImage.new(:data=><<EOD)
20
#define up_arrow_width 9
21
#define up_arrow_height 9
22
static unsigned char down_arrow_bits[] = {
23
0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x38, 0x00,
24
0x10, 0x00, 0x10, 0x00, 0x00, 0x00};
27
############################
29
############################
30
def initialize_composite(keys={})
31
keys = _symbolkey2str(keys)
33
@initwait = keys.delete('startwait'){300}
34
@interval = keys.delete('interval'){150}
35
@initwait -= @interval
36
@initwait = 0 if @initwait < 0
38
@lbox = TkListbox.new(@frame, :borderwidth=>0)
40
TkPack.propagate(@lbox, false)
42
@scr = TkScrollbar.new(@frame, :width=>10)
44
@lbox.yscrollcommand(proc{|*args| @scr.set(*args); _config_proc})
45
@scr.command(proc{|*args| @lbox.yview(*args); _config_proc})
47
@up_arrow = TkLabel.new(@lbox, :image=>@@up_bmp,
48
:relief=>:raised, :borderwidth=>1)
49
@down_arrow = TkLabel.new(@lbox, :image=>@@down_bmp,
50
:relief=>:raised, :borderwidth=>1)
54
@lbox.pack(:side=>:left, :fill=>:both, :expand=>:true)
56
delegate('DEFAULT', @lbox)
57
delegate('background', @frame, @scr)
58
delegate('activebackground', @scr)
59
delegate('troughcolor', @scr)
60
delegate('repeatdelay', @scr)
61
delegate('repeatinterval', @scr)
62
delegate('relief', @frame)
63
delegate('borderwidth', @frame)
65
delegate_alias('arrowrelief', 'relief', @up_arrow, @down_arrow)
66
delegate_alias('arrowborderwidth', 'borderwidth', @up_arrow, @down_arrow)
68
scrollbar(keys.delete('scrollbar')){false}
70
configure keys unless keys.empty?
74
unless @up_arrow.winfo_mapped?
75
@up_arrow.pack(:side=>:top, :fill=>:x)
80
unless @down_arrow.winfo_mapped?
81
@down_arrow.pack(:side=>:bottom, :fill=>:x)
87
@lbox.selection_clear(0, 'end')
88
@lbox.selection_set(idx)
91
def _check_sel(cidx, tidx = nil, bidx = nil)
94
tidx = @lbox.nearest(0)
98
bidx = @lbox.nearest(10000)
99
bidx -= 1 if bidx < @lbox.index('end') - 1
110
cidx = @lbox.curselection[0]
111
idx = @lbox.nearest(0)
115
@up_arrow.pack_forget if idx == 1
116
@up_timer.stop if idx == 0
117
_show_down_arrow if @lbox.bbox('end') == []
119
if cidx && cidx > 0 && (idx == 0 || cidx == @lbox.nearest(10000))
125
cidx = @lbox.curselection[0]
126
eidx = @lbox.index('end') - 1
127
idx = @lbox.nearest(10000)
130
_set_sel(cidx + 1) if cidx < eidx
131
@down_arrow.pack_forget if idx + 1 == eidx
132
@down_timer.stop if idx == eidx
133
_show_up_arrow if @lbox.bbox(0) == []
135
if cidx && cidx < eidx && (eidx == idx || cidx == @lbox.nearest(0))
141
cidx = @lbox.curselection[0]
142
_set_sel(cidx = @lbox.index('activate')) unless cidx
145
@up_arrow.pack_forget
146
elsif cidx == @lbox.nearest(0)
152
cidx = @lbox.curselection[0]
153
_set_sel(cidx = @lbox.index('activate')) unless cidx
155
if cidx == @lbox.index('end') - 1
156
@down_arrow.pack_forget
157
elsif cidx == @lbox.nearest(10000)
164
@up_arrow.pack_forget
165
@down_arrow.pack_forget
168
tidx = @lbox.nearest(0)
169
bidx = @lbox.nearest(10000)
174
@up_arrow.pack_forget unless @up_timer.running?
176
if bidx < @lbox.index('end') - 1
180
@down_arrow.pack_forget unless @down_timer.running?
182
cidx = @lbox.curselection[0]
183
_check_sel(cidx, tidx, bidx) if cidx
187
@up_timer = TkAfter.new(@interval, -1, proc{_up_proc})
188
@down_timer = TkAfter.new(@interval, -1, proc{_down_proc})
190
@up_timer.set_start_proc(@initwait, proc{})
191
@down_timer.set_start_proc(@initwait, proc{})
193
@up_arrow.bind('Enter', proc{@up_timer.start})
194
@up_arrow.bind('Leave', proc{@up_timer.stop if @up_arrow.winfo_mapped?})
195
@down_arrow.bind('Enter', proc{@down_timer.start})
196
@down_arrow.bind('Leave', proc{@down_timer.stop if @down_arrow.winfo_mapped?})
198
@lbox.bind('Configure', proc{_config_proc})
199
@lbox.bind('Enter', proc{|y| _set_sel(@lbox.nearest(y))}, '%y')
200
@lbox.bind('Motion', proc{|y|
201
@up_timer.stop if @up_timer.running?
202
@down_timer.stop if @down_timer.running?
203
_check_sel(@lbox.nearest(y))
206
@lbox.bind('Up', proc{_key_UP_proc})
207
@lbox.bind('Down', proc{_key_DOWN_proc})
210
############################
212
############################
215
@scr.pack(:side=>:right, :fill=>:y)
222
################################################
224
class TkCombobox < TkEntry
227
@@down_btn_bmp = TkBitmapImage.new(:data=><<EOD)
228
#define down_arrow_width 11
229
#define down_arrow_height 11
230
static unsigned char down_arrow_bits[] = {
231
0x00, 0x00, 0xfe, 0x03, 0xfc, 0x01, 0xfc, 0x01, 0xf8, 0x00, 0xf8, 0x00,
232
0x70, 0x00, 0x70, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00};
235
@@up_btn_bmp = TkBitmapImage.new(:data=><<EOD)
236
#define up_arrow_width 11
237
#define up_arrow_height 11
238
static unsigned char up_arrow_bits[] = {
239
0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00, 0x70, 0x00, 0xf8, 0x00,
240
0xf8, 0x00, 0xfc, 0x01, 0xfc, 0x01, 0xfe, 0x03, 0x00, 0x00};
243
def _button_proc(dir = true)
245
x = @frame.winfo_rootx
246
y = @frame.winfo_rooty
248
@top.geometry("+#{x}+#{y + @frame.winfo_height}")
250
@btn.image(@@up_btn_bmp)
251
@top.geometry("+#{x}+#{y - @top.winfo_reqheight}")
256
if (idx = values.index(@ent.value))
259
@lst.selection_set(idx)
263
@lst.selection_set(0)
269
if (idx = @var.to_i) >= 0
270
@ent.value = @lst.get(idx)
274
@btn.image(@@down_btn_bmp)
284
private :_button_proc
287
@btn.bind('1', proc{_button_proc(true)})
288
@btn.bind('3', proc{_button_proc(false)})
290
@lst.bind('1', proc{|y| @var.value = @lst.nearest(y)}, '%y')
291
@lst.bind('Return', proc{@var.value = @lst.curselection[0]})
293
cancel = TkVirtualEvent.new('2', '3', 'Escape')
294
@lst.bind(cancel, proc{@var.value = -1})
296
private :_init_bindings
298
def initialize_composite(keys={})
299
keys = _symbolkey2str(keys)
301
@btn = TkLabel.new(@frame, :relief=>:raised, :borderwidth=>3,
302
:image=>@@down_btn_bmp).pack(:side=>:right,
303
:ipadx=>2, :fill=>:y)
304
@ent = TkEntry.new(@frame).pack(:side=>:left)
307
@top = TkToplevel.new(@btn, :borderwidth=>1, :relief=>:raised) {
310
overrideredirect(true)
313
startwait = keys.delete('startwait'){300}
314
interval = keys.delete('interval'){150}
315
@lst = TkAutoScrollbox.new(@top,
316
:startwait=>startwait,
317
:interval=>interval).pack(:fill=>:both,
321
@var = TkVariable.new
325
delegate('DEFAULT', @ent)
326
delegate('height', @lst)
327
delegate('relief', @frame)
328
delegate('borderwidth', @frame)
330
delegate('arrowrelief', @lst)
331
delegate('arrowborderwidth', @lst)
333
if mode = keys.delete('scrollbar')
337
configure keys unless keys.empty?
339
private :initialize_composite
347
@lst.get(0, 'end').each{|l| len = l.length if l.length > len}
350
private :_reset_width
354
unless @ent_list.index(ent)
356
@lst.insert('end', ent)
364
@ent_list.delete(ent)
365
if idx = @lst.get(0, 'end').index(ent)
372
def values(ary = nil)
374
@lst.delete(0, 'end')
376
ary.each{|ent| add(ent)}
385
@lst.see(@lst.index(idx) - 1)
394
################################################
396
################################################
399
e = TkCombobox.new(:height=>7, :scrollbar=>true, :textvariable=>v,
400
:arrowrelief=>:flat, :arrowborderwidth=>0,
401
:startwait=>400, :interval=>200).pack
402
e.values(%w(aa bb cc dd ee ff gg hh ii jj kk ll mm nn oo pp qq rr ss tt uu))
403
#e.see(e.list_index('end') - 2)
406
fnt = TkFont.new('Helvetica 10')
407
TkLabel.new(f, :font=>fnt, :text=>'TkCombobox value :').pack(:side=>:left)
408
TkLabel.new(f, :font=>fnt, :textvariable=>v).pack(:side=>:left)
411
TkFrame.new(:relief=>:raised, :borderwidth=>2,
412
:height=>3).pack(:fill=>:x, :expand=>true, :padx=>5, :pady=>3)
414
l = TkAutoScrollbox.new(nil, :relief=>:groove, :borderwidth=>4,
415
:width=>20).pack(:fill=>:both, :expand=>true)
416
(0..20).each{|i| l.insert('end', "line #{i}")}
418
TkFrame.new(:relief=>:ridge, :borderwidth=>3){
419
TkButton.new(self, :text=>'ON',
420
:command=>proc{l.scrollbar(true)}).pack(:side=>:left)
421
TkButton.new(self, :text=>'OFF',
422
:command=>proc{l.scrollbar(false)}).pack(:side=>:right)