lib/ruiby_gtk/dsl/form_fields.rb
# Creative Commons BY-SA : Regis d'Aubarede <regis.aubarede@gmail.com>
# LGPL
module Ruiby_dsl
############### Inputs widgets
# combo box.
# Choices are describes with:
# * a Hash choice-text => value-of-choice
# * or an array of string : value of choice is the index of choice in array
#
# default: initiale choice, String (text of choice) or index of choice in array/hash of choices
# bloc : called when a choice is selected, with text and value
#
# methods defined:
# * cb.get_selection() >> [text-selected, value-of-selected] or ['',-1]
#
# Usage :
# combo(%w{aa bb cc},"bb") { |text,index| alert("#{text} at #{index}") }
# w=combo({"aa" => 20, "bb"=> 30, "cc"=> 40},0) { |text,index| alert("#{text} at #{index}") }
# w.get_selection() ==> ["aa",20]
def combo(choices,default=nil,option={},&blk)
# TODO Dyn
w=ComboBoxText.new()
choices=choices.inject({}) { |h,k|
h[k]=h.size
h
} if Array===choices
default=choices.to_a.first.first unless default
inv_choices=choices.values.each_with_index.inject({}) {|h,(ind,i)| h[ind]=i ; h}
choices.each do |text,indice|
w.append_text(text)
end
selection=default.is_a?(String) ? inv_choices[choices[default]] : default.to_i
w.set_active(selection)
w.signal_connect(:changed) { |w,evt|
indice=choices[w.active_text]
w._set_selection(w.active_text,indice)
p w.get_selection()
blk.call(w.active_text,indice) if blk
}
attribs(w,option)
class << w
def _set_selection(t,i) @selection=[t,i] end # done on changed signal
def get_selection() p @selection; (@selection||["",-1]) end
end
w._set_selection(default||"",choices[default])
w
end
# two state button, with text for each state and a initiale value
# value can be read by w.active?
# value can be changed by w.set_active(true/false)
# callback is called on state change, with new value as argument
def toggle_button(text1,text2=nil,value=false,option={},&blk)
if DynVar === value
return _dyn_toggle_button(text1,text2,value,option,&blk)
end
text2 = "- "+text1 unless text2
b=ToggleButton.new(text1);
b.signal_connect("clicked") do |w,e|
w.label= w.active?() ? text2.to_s : text1.to_s
( blk.call(w.active?()) rescue error($!) ) if blk
end
b.set_active(value)
b.label= value ? text2.to_s : text1.to_s
attribs(b,option)
b
end
def _dyn_toggle_button(text1,text2,var,option={},&blk)
text2 = "- "+text1 unless text2
b=ToggleButton.new(label: text1);
b.signal_connect("clicked") do |w,e|
w.label= w.active?() ? text2.to_s : text1.to_s
( blk.call(w.active?()) rescue error($!) ) if blk
var.value=w.active?()
end
b.set_active(var.value)
var.observ { |v| b.set_active(var.get_as_bool()) }
b.label= var.value ? text2.to_s : text1.to_s
attribs(b,option)
b
end
def _dyn_check_button(text,var,option={})
w= block_given? ? check_button(text,!! var.value,option) : check_button(text,!! var.value,option) { |v| var.set_as_bool(v) }
var.observ { |v| w.set_active(var.get_as_bool()) }
w
end
# create a checked button
# state can be read by cb.active?
def check_button(text="",value=false,option={},&blk)
if DynVar === value
return _dyn_check_button(text,value,option)
end
b=CheckButton.new(text)
b.set_active(value)
b.signal_connect("clicked") do |w,e|
( blk.call(w.active?()) rescue error($!) ) if blk
end
attribs(b,option)
b
end
# create a liste of radio button, vertically disposed
# value is the indice of active item (0..(n-1)) at creation time
# define 2 methods:
# * get_selected # get indice of active radio-button
# * set_selected(indice) # set indice of active radio-button
def vradio_buttons(ltext=["empty!"],value=-1) _radio_buttons(:vertical,ltext,value) end
# as vradio_buttons , but horizontaly disposed
def hradio_buttons(ltext=["empty!"],value=-1) _radio_buttons(:horizontal,ltext,value) end
def _radio_buttons(sens,ltext=["empty!"],value=-1,&blk)
is_dyn = (DynVar === value)
b0=nil
s=var_box(sens,{},false) {
ltext.each_with_index {|txt,i|
b= (i==0) ? (b0=RadioButton.new(label: txt)) : RadioButton.new(member: b0,label: txt)
attribs(b,{})
b.signal_connect("clicked") do |w,e|
puts "clicked on button #{i} state=#{w.active?}"
if w.active?
( blk.call(i) rescue error($!) ) if blk
puts "action on button #{i}"
(puts "set to #{i} from #{value.value}"; value.value=i ) if is_dyn && value.value.to_i!=i
end
end
if i== (is_dyn ? value.value : value)
#b.toggled
b.set_active(true)
end
}
}
# TODO: test!
class << s
; def set_b0(b) @b0=b end
; def b0() @b0 end
; def get_selected()
b0.group.each_with_index { |w,index| return(index) if w.active? }
end
; def set_selected(indice)
b0.group.reverse.each_with_index { |w,index|
if !w.active? && indice.to_i==index
puts "setsel #{index}"
w.set_active(true) rescue p $!
return
end
}
end
end
s.set_b0(b0)
if is_dyn
value.set_trace(true)
value.observ { |v| after(100) {p ["observ",v] ;s.set_selected(v.to_i) }}
end
attribs(s,{})
end
def _dyn_entry(var,size,options,slotied)
size= var.value.to_s.size*2 unless size
w= unless slotied
(block_given? ? entry(var.value,size,options) : entry(var.value,size,options) { |v| var.value=v })
else
(block_given? ? entry(var.value,size,options) : entry(var.value,size,options) { |v| var.value=v })
end
var.observ { |v| w.text = v.to_s }
w
end
# create a text entry for keyboard input
# if block defined, it while be trigger on eech of (character) change of the entry
def entry(value,size=10,option={},&blk)
if DynVar === value
return _dyn_entry(value,size,option,false,&blk)
end
w=Entry.new().tap {|e| e.set_text(value ? value.to_s : "") }
w.set_width_chars(size)
after(1) do
w.signal_connect("key-press-event") do |en,e|
after(1) { blk.call(w.text) rescue error($!) }
false
end
end if block_given?
attribs(w,option)
end
def _dyn_ientry(var,options,slotied)
w= unless slotied
(block_given? ? ientry(var.value,options) : ientry(var.value,options) { |v| var.value=v })
else
(block_given? ? ientry(var.value,options) : ientry(var.value,options) { |v| var.value=v })
end
var.observ { |v| w.text = v.to_s }
w
end
# create a integer text entry for keyboed input
# option must define :min :max :by for spin button
def ientry(value,option={},&blk)
if DynVar === value
return _dyn_entry(value,value.value.to_s.size*4,option,true,&blk)
end
w=SpinButton.new(option[:min].to_i,option[:max].to_i,option[:by]||1)
w.set_numeric(true)
w.set_value(value ? value.to_i : 0)
w.signal_connect("value-changed") do |en|
after(1) { blk.call(w.value) }
false
end if block_given?
attribs(w,option)
w
end
# create a integer text entry for keyboed input
# option must define :min :max :by for spin button
def fentry(value,option={},&blk)
# TODO Dyn
w=SpinButton.new(option[:min].to_f,option[:max].to_f,option[:by].to_f)
w.set_numeric(true)
w.set_value(value ? value.to_f : 0.0)
w.signal_connect("value-changed") do |en|
after(1) { blk.call(w.value) rescue error($!) }
false
end if block_given?
attribs(w,option)
w
end
# show a label and a entry in a flow. entry widget is returned
# see fields()
def field(tlabel,lwidth,value,option={},&blk)
e=nil
flow {
l=label(tlabel+ " : ")
l.width_chars=lwidth+3
e=entry(value,10,option,&blk)
}
e
end
# show a stack of label/entry and buttons validation/annulation
# on button, bloc is invoked with the list of values of entrys
def fields(alabel=[["nothing",""]],option={},&blk)
size=alabel.map {|t| t[0].size}.max
stack do
le=alabel.map { |(label,value)| field(label,size,value) }
if block_given?
flowi {
button("Validation") { blk.call(*le.map {|t| t.text}) }
button("Annulation") { blk.call(*le.map {|t| nil}) }
}
end
end
end
def _dyn_islider(var,option,&blk)
w= block_given? ? islider(var.value.to_i,option,&blk) : islider(var.value.to_i,option) { |v| var.value=v.to_i }
var.observ { |v| w.set_value(v.to_i) }
attribs(w,option)
w
end
# create a slider
# option must define :min :max :by for spin button
# current value can be read by w.value
# if bloc is given, it with be call on each change, with new value as parameter
# if value is a DynVar, slider will be binded to the DynVar : each change of the var value will update the slider,
# of no block given,each change of the slider is notifies to the DynVar, else change will
# only call the block.
def islider(value=0,option={},&b)
if DynVar === value
return _dyn_islider(value,option,&b)
end
w=Scale.new(:horizontal,(option[:min]||0).to_i,(option[:max]||100).to_i,option[:by]||1)
w.set_value(value ? value.to_i : 0)
w.signal_connect(:value_changed) { || b.call(w.value) rescue error($!) } if block_given?
attribs(w,option)
end
# Progress bar
# w=progress(0.5,"Hello")
# w.set_fraction(0.99)
# show a progress bar. progresion is updated by set_fraction(0.0..1.0)
# w=progress(0.5,"Hello")
# w.set_fraction(0.99)
def progress(value=0,text=nil,option={})
if DynVar === value
return _dyn_progress(value,text,option)
end
w=ProgressBar.new()
w.set_fraction(value)
w.text=text if text
#w.orientation=ProgressBar::GtkProgressBarOrientation::LEFT_TO_RIGHT
attribs(w,option)
end
def _dyn_progress(var,text,option)
w=progress(var.value,text,option)
var.observ {|value| w.set_fraction(value.to_f)}
w
end
# show a dialog with a progress bar, actualised by a Dynvar value
# dv=panel_progress("Loading xxxxx.rb...")
# anim(100) { fract=Time.now.to_i%60/60.0 ; dv.value=fract }
# after(10000) { dv.value=-1 }
# dv=panel_progress("Starting...") { |value|
# "Advance: %d " % ((value*100).to_i " }
# }
def panel_progress(text="",&blk)
dvar= ::DynVar.new(0)
lw=nil
d=panel_async("Progression...") {
lw=label text if text && text.size >0
label ""
flowi { labeli " " ; progress(dvar,text) ; labeli " "}
label ""
}
dvar.observ {|value|
if blk && lw
lw.text= blk.call(value)
end
if value <= -1.0 || value >= 2.0
dvar.destroy
d.destroy
end
}
dvar
end
# create a button wich will show a dialog for color choice
# if bloc is given, it with be call on each change, with new color value as parameter
# current color is w.get_color()
def color_choice(text=nil,options={},&cb)
but,lab=nil,nil
out=flow {
but = button((text||"Color?...").to_s) do
c=ask_color
apply_options(lab,{bg: c}) if c
cb.call(c) if block_given?
end
lab=label(" c ")
}
attribs(but,options)
def out.get_color()
chilldren[1].get_color()
end
out
end
# multiline entry on dynvar
#
def text_area_dyn(dynvar,w=200,h=100,args={}) # from green_shoes app
# TDODO : test !
w=text_area(w,h,args)
dynvar.observ { |o,n| w.text=n }
w.text_area.signal_connect(:changed) { |t,e| dynvar.value(w.text) }
w
end
# multiline entry
# w=text_area(min_width,min_height,options)
#
# Some binding are defined :
# * w.text_area ; get text area widdget (w is a ScrolledWindow)
# * w.text="" ; set content
# * puts w.text() ; get content
# * w.append("data \n") ; append conent to the end of current content
# * w.text_area.wrap_mode = :none/:word
def text_area(w=200,h=100,args={}) # from green_shoes app
tv = Gtk::TextView.new
tv.wrap_mode = :word
tv.buffer.text = args[:text].to_s if args[:text]
tv.override_font(Pango::FontDescription.new(args[:font])) if args[:font]
tv.accepts_tab = true
eb = Gtk::ScrolledWindow.new
eb.set_size_request(w,h)
eb.add(tv)
eb.define_singleton_method(:text_area) { tv }
class << eb
; def text=(a) self.children[0].buffer.text=a.to_s.encode("UTF-8",'binary', invalid: :replace, undef: :replace, replace: '?') end
; def text() self.children[0].buffer.text end
; def append(a) self.children[0].buffer.text+=a.to_s.encode("UTF-8",'binary', invalid: :replace, undef: :replace, replace: '?') end
; def buffer() self.children[0].buffer end
; def tv() self.children[0] end
end
eb.show_all
args.delete(:text)
args.delete(:font)
attribs(tv,args)
attribs(eb,args)
end
end