From 615a54e77d00c0b456fe6a6f8b6d48203e25b3ac Mon Sep 17 00:00:00 2001 From: nagai Date: Sat, 22 Nov 2003 13:52:48 +0000 Subject: * ext/tk/lib/tk.rb: add Tk.grab_release and fix bug of TkComposite * ext/tk/lib/tkafter.rb: bug fix * ext/tk/sample/tkcombobox.rb: new sample script * ext/tcltklib/tcltklib.c: add native thread check git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5007 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/tcltklib/tcltklib.c | 4 +- ext/tk/MANIFEST | 1 + ext/tk/lib/tk.rb | 7 +- ext/tk/lib/tkafter.rb | 2 +- ext/tk/sample/tkcombobox.rb | 418 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 428 insertions(+), 4 deletions(-) create mode 100644 ext/tk/sample/tkcombobox.rb (limited to 'ext') diff --git a/ext/tcltklib/tcltklib.c b/ext/tcltklib/tcltklib.c index d20c24eb22..50c654d814 100644 --- a/ext/tcltklib/tcltklib.c +++ b/ext/tcltklib/tcltklib.c @@ -503,7 +503,9 @@ lib_eventloop_core(check_root, check_var) rb_trap_exec(); } else { DUMP1("thread scheduling"); - rb_thread_schedule(); + if (is_ruby_native_thread()) { + rb_thread_schedule(); + } } } return 1; diff --git a/ext/tk/MANIFEST b/ext/tk/MANIFEST index f86797b5fb..96f7e744f7 100644 --- a/ext/tk/MANIFEST +++ b/ext/tk/MANIFEST @@ -26,6 +26,7 @@ sample/safe-tk.rb sample/tkballoonhelp.rb sample/tkbiff.rb sample/tkbrowse.rb +sample/tkcombobox.rb sample/tkdialog.rb sample/tkfrom.rb sample/tkhello.rb diff --git a/ext/tk/lib/tk.rb b/ext/tk/lib/tk.rb index cec589047f..308e6ddfd8 100644 --- a/ext/tk/lib/tk.rb +++ b/ext/tk/lib/tk.rb @@ -4261,6 +4261,9 @@ class TkWindow<<0) + @path = @lbox.path + TkPack.propagate(@lbox, false) + + @scr = TkScrollbar.new(@frame, :width=>10) + + @lbox.yscrollcommand(proc{|*args| @scr.set(*args); _config_proc}) + @scr.command(proc{|*args| @lbox.yview(*args); _config_proc}) + + @up_arrow = TkLabel.new(@lbox, :image=>@@up_bmp, + :relief=>:raised, :borderwidth=>1) + @down_arrow = TkLabel.new(@lbox, :image=>@@down_bmp, + :relief=>:raised, :borderwidth=>1) + + _init_binding + + @lbox.pack(:side=>:left, :fill=>:both, :expand=>:true) + + delegate('DEFAULT', @lbox) + delegate('background', @frame, @scr) + delegate('activebackground', @scr) + delegate('troughcolor', @scr) + delegate('repeatdelay', @scr) + delegate('repeatinterval', @scr) + delegate('relief', @frame) + delegate('borderwidth', @frame) + + scrollbar(keys.delete('scrollbar')){false} + + configure keys unless keys.empty? + end + + ############################ + private + ############################ + def _show_up_arrow + unless @up_arrow.winfo_mapped? + @up_arrow.pack(:side=>:top, :fill=>:x) + end + end + + def _show_down_arrow + unless @down_arrow.winfo_mapped? + @down_arrow.pack(:side=>:bottom, :fill=>:x) + end + end + + def _set_sel(idx) + @lbox.activate(idx) + @lbox.selection_clear(0, 'end') + @lbox.selection_set(idx) + end + + def _check_sel(cidx, tidx = nil, bidx = nil) + _set_sel(cidx) + unless tidx + tidx = @lbox.nearest(0) + tidx += 1 if tidx > 0 + end + unless bidx + bidx = @lbox.nearest(10000) + bidx -= 1 if bidx < @lbox.index('end') - 1 + end + if cidx > bidx + _set_sel(bidx) + end + if cidx < tidx + _set_sel(tidx) + end + end + + def _up_proc + cidx = @lbox.curselection[0] + idx = @lbox.nearest(0) + if idx >= 0 + @lbox.see(idx - 1) + _set_sel(idx) + @up_arrow.pack_forget if idx == 1 + @up_timer.stop if idx == 0 + _show_down_arrow if @lbox.bbox('end') == [] + end + if cidx && cidx > 0 && (idx == 0 || cidx == @lbox.nearest(10000)) + _set_sel(cidx - 1) + end + end + + def _down_proc + cidx = @lbox.curselection[0] + eidx = @lbox.index('end') - 1 + idx = @lbox.nearest(10000) + if idx <= eidx + @lbox.see(idx + 1) + _set_sel(cidx + 1) if cidx < eidx + @down_arrow.pack_forget if idx + 1 == eidx + @down_timer.stop if idx == eidx + _show_up_arrow if @lbox.bbox(0) == [] + end + if cidx && cidx < eidx && (eidx == idx || cidx == @lbox.nearest(0)) + _set_sel(cidx + 1) + end + end + + def _key_UP_proc + cidx = @lbox.curselection[0] + _set_sel(cidx = @lbox.index('activate')) unless cidx + cidx -= 1 + if cidx == 0 + @up_arrow.pack_forget + elsif cidx == @lbox.nearest(0) + @lbox.see(cidx - 1) + end + end + + def _key_DOWN_proc + cidx = @lbox.curselection[0] + _set_sel(cidx = @lbox.index('activate')) unless cidx + cidx += 1 + if cidx == @lbox.index('end') - 1 + @down_arrow.pack_forget + elsif cidx == @lbox.nearest(10000) + @lbox.see(cidx + 1) + end + end + + def _config_proc + if @lbox.size == 0 + @up_arrow.pack_forget + @down_arrow.pack_forget + return + end + tidx = @lbox.nearest(0) + bidx = @lbox.nearest(10000) + if tidx > 0 + _show_up_arrow + tidx += 1 + else + @up_arrow.pack_forget unless @up_timer.running? + end + if bidx < @lbox.index('end') - 1 + _show_down_arrow + bidx -= 1 + else + @down_arrow.pack_forget unless @down_timer.running? + end + cidx = @lbox.curselection[0] + _check_sel(cidx, tidx, bidx) if cidx + end + + def _init_binding + @up_timer = TkAfter.new(@interval, -1, proc{_up_proc}) + @down_timer = TkAfter.new(@interval, -1, proc{_down_proc}) + + @up_timer.set_start_proc(@initwait, proc{}) + @down_timer.set_start_proc(@initwait, proc{}) + + @up_arrow.bind('Enter', proc{@up_timer.start}) + @up_arrow.bind('Leave', proc{@up_timer.stop if @up_arrow.winfo_mapped?}) + @down_arrow.bind('Enter', proc{@down_timer.start}) + @down_arrow.bind('Leave', proc{@down_timer.stop if @down_arrow.winfo_mapped?}) + + @lbox.bind('Configure', proc{_config_proc}) + @lbox.bind('Enter', proc{|y| _set_sel(@lbox.nearest(y))}, '%y') + @lbox.bind('Motion', proc{|y| + @up_timer.stop if @up_timer.running? + @down_timer.stop if @down_timer.running? + _check_sel(@lbox.nearest(y)) + }, '%y') + + @lbox.bind('Up', proc{_key_UP_proc}) + @lbox.bind('Down', proc{_key_DOWN_proc}) + end + + ############################ + public + ############################ + def scrollbar(mode) + if mode + @scr.pack(:side=>:right, :fill=>:y) + else + @scr.pack_forget + end + end +end + +################################################ + +class TkCombobox < TkEntry + include TkComposite + + @@down_btn_bmp = TkBitmapImage.new(:data=><< 0 + @lst.see(0) + @lst.activate(0) + @lst.selection_set(0) + end + @top.grab + + begin + @var.tkwait + if (idx = @var.to_i) >= 0 + @ent.value = @lst.get(idx) + end + @top.withdraw + @btn.relief(:raised) + @btn.image(@@down_btn_bmp) + rescue + ensure + begin + @top.grab(:release) + @ent.focus + rescue + end + end + end + private :_button_proc + + def _init_bindings + @btn.bind('1', proc{_button_proc(true)}) + @btn.bind('3', proc{_button_proc(false)}) + + @lst.bind('1', proc{|y| @var.value = @lst.nearest(y)}, '%y') + @lst.bind('Return', proc{@var.value = @lst.curselection[0]}) + + cancel = TkVirtualEvent.new('2', '3', 'Escape') + @lst.bind(cancel, proc{@var.value = -1}) + end + private :_init_bindings + + def initialize_composite(keys={}) + keys = _symbolkey2str(keys) + + @btn = TkLabel.new(@frame, :relief=>:raised, :borderwidth=>3, + :image=>@@down_btn_bmp).pack(:side=>:right, + :ipadx=>2, :fill=>:y) + @ent = TkEntry.new(@frame).pack(:side=>:left) + @path = @ent.path + + @top = TkToplevel.new(@btn, :borderwidth=>1, :relief=>:raised) { + withdraw + transient + overrideredirect(true) + } + + startwait = keys.delete('startwait'){300} + interval = keys.delete('interval'){150} + @lst = TkAutoScrollbox.new(@top, + :startwait=>startwait, + :interval=>interval).pack(:fill=>:both, + :expand=>true) + @ent_list = [] + + @var = TkVariable.new + + _init_bindings + + delegate('DEFAULT', @ent) + delegate('height', @lst) + delegate('relief', @frame) + delegate('borderwidth', @frame) + + if mode = keys.delete('scrollbar') + scrollbar(mode) + end + + configure keys unless keys.empty? + end + + def scrollbar(mode) + @lst.scrollbar(mode) + end + + def _reset_width + len = @ent.width + @lst.get(0, 'end').each{|l| len = l.length if l.length > len} + @lst.width(len + 1) + end + private :_reset_width + + def add(ent) + ent = ent.to_s + unless @ent_list.index(ent) + @ent_list << ent + @lst.insert('end', ent) + end + _reset_width + self + end + + def remove(ent) + ent = ent.to_s + @ent_list.delete(ent) + if idx = @lst.get(0, 'end').index(ent) + @lst.delete(idx) + end + _reset_width + self + end + + def values(ary = nil) + if ary + @lst.delete(0, 'end') + @ent_list.clear + ary.each{|ent| add(ent)} + _reset_width + self + else + @lst.get(0, 'end') + end + end + + def see(idx) + @lst.see(@lst.index(idx) - 1) + end + + def list_index(idx) + @lst.index(idx) + end +end + + +################################################ +# test +################################################ +if __FILE__ == $0 + v = TkVariable.new + e = TkCombobox.new(:height=>7, :scrollbar=>true, :textvariable=>v, + :startwait=>400, :interval=>200).pack + e.values(%w(aa bb cc dd ee ff gg hh ii jj kk ll mm nn oo pp qq rr ss tt uu)) + #e.see(e.list_index('end') - 2) + e.value = 'cc' + TkFrame.new{|f| + fnt = TkFont.new('Helvetica 10') + TkLabel.new(f, :font=>fnt, :text=>'TkCombobox value :').pack(:side=>:left) + TkLabel.new(f, :font=>fnt, :textvariable=>v).pack(:side=>:left) + }.pack + + TkFrame.new(:relief=>:raised, :borderwidth=>2, + :height=>3).pack(:fill=>:x, :expand=>true, :padx=>5, :pady=>3) + + l = TkAutoScrollbox.new(nil, :relief=>:groove, :borderwidth=>4, + :width=>20).pack(:fill=>:both, :expand=>true) + (0..20).each{|i| l.insert('end', "line #{i}")} + + TkFrame.new(:relief=>:ridge, :borderwidth=>3){ + TkButton.new(self, :text=>'ON', + :command=>proc{l.scrollbar(true)}).pack(:side=>:left) + TkButton.new(self, :text=>'OFF', + :command=>proc{l.scrollbar(false)}).pack(:side=>:right) + pack(:fill=>:x) + } + Tk.mainloop +end -- cgit v1.2.3