Module: Ruiby_dsl

Includes:
Gtk, Ruiby_default_dialog
Included in:
AbstractComposant, Ruiby, Ruiby_dialog, Ruiby_gtk
Defined in:
lib/ruiby_gtk/dsl/plot.rb,
lib/ruiby_gtk/systray.rb,
lib/ruiby_gtk/component.rb,
lib/ruiby_gtk/dsl/table.rb,
lib/ruiby_gtk/dsl/canvas.rb,
lib/ruiby_gtk/dsl/pixmap.rb,
lib/ruiby_gtk/dsl/script.rb,
lib/ruiby_gtk/ruiby_dsl3.rb,
lib/ruiby_gtk/ruiby_dsl3.rb,
lib/ruiby_gtk/dsl/editors.rb,
lib/ruiby_gtk/dsl/layouts.rb,
lib/ruiby_gtk/dsl/commands.rb,
lib/ruiby_gtk/dsl/list_grid.rb,
lib/ruiby_gtk/dsl/form_fields.rb,
lib/ruiby_gtk/dsl/menus_popup.rb,
lib/ruiby_gtk/dsl/label_button_image.rb

Overview

Creative Commons BY-SA : Regis d'Aubarede <regis.aubarede@gmail.com> LGPL

Defined Under Namespace

Classes: HandlerContainer

Constant Summary

GTK2ICONNAME =
{
"about" => "help-about", "add" => "list-add", "bold" => "format-text-bold", "cancel" => "process-stop", "clear" => "edit-clear", 
"close" => "window-close", "copy" => "edit-copy", "cut" => "edit-cut", "delete" => "edit-delete", "execute" => "system-run", 
"find-and-replace" => "edit-find-replace", "find" => "edit-find", "fullscreen" => "view-fullscreen", "go-back-ltr" => "go-previous",
"go-back-rtl" => "go-next", "go-down" => "go-down", "go-forward-ltr" => "go-next", "go-forward-rtl" => "go-previous", "go-up" => "go-up",
"goto-bottom" => "go-bottom", "goto-first-ltr" => "go-first", "goto-first-rtl" => "go-last", "goto-last-ltr" => "go-last",
"goto-last-rtl" => "go-first", "goto-top" => "go-top", "help" => "help-contents", "home" => "go-home", "indent-ltr" => "format-indent-more",
"indent-rtl" => "format-indent-less", "italic" => "format-text-italic", "jump-to-ltr" => "go-jump", "jump-to-rtl" => "go-jump",
"justify-center" => "format-justify-center", "justify-fill" => "format-justify-fill", "justify-left" => "format-justify-left",
"justify-right" => "format-justify-right", "leave-fullscreen" => "view-restore", "media-forward-ltr" => "media-seek-forward",
"media-forward-rtl" => "media-seek-backward", "media-next-ltr" => "media-skip-forward", "media-next-rtl" => "media-skip-backward",
"media-pause" => "media-playback-pause", "media-play-ltr" => "media-playback-start", "media-previous-ltr" => "media-skip-backward",
"media-previous-rtl" => "media-skip-forward", "media-record" => "media-record", "media-rewind-ltr" => "media-seek-backward",
"media-rewind-rtl" => "media-seek-forward", "media-stop" => "media-playback-stop", "new" => "document-new", "open" => "document-open",
"paste" => "edit-paste", "print-preview" => "document-print-preview", "print" => "document-print", "properties" => "document-properties",
"quit" => "application-exit", "redo-ltr" => "edit-redo", "refresh" => "view-refresh", "remove" => "list-remove",
"revert-to-saved-ltr" => "document-revert", "revert-to-saved-rtl" => "document-revert", "save-as" => "document-save-as",
"save" => "document-save", "select-all" => "edit-select-all", "sort-ascending" => "view-sort-ascending",
"sort-descending" => "view-sort-descending", "spell-check" => "tools-check-spelling", "stop" => "process-stop",
"strikethrough" => "format-text-strikethrough", "underline" => "format-text-underline", "undo-ltr" => "edit-undo",
"unindent-ltr" => "format-indent-less", "unindent-rtl" => "format-indent-more", "zoom-100" => "zoom-original",
"zoom-fit" => "zoom-fit-best" }

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Ruiby_default_dialog

#_gooooooooooo, #_process_terminal_key, #alert, #ask, #ask_color, #ask_dir_to_read, #ask_dir_to_write, #ask_file_to_read, #ask_file_to_write, #dialog_chooser, #edit, #error, #message, #prompt, #promptSync, #terminal

Class Method Details

.cv_color_html(html_color, opacity = 1) ⇒ Object

parse html color ( “#FF00AA” ) to rgba array, useful for canvas vectors styles



235
236
237
238
239
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 235

def self.cv_color_html(html_color,opacity=1)
  c=::Gdk::Color.parse(html_color.kind_of?(Array) ? html_color.last : html_color)
  #::Gdk::RGBA.new(c.red/65000.0,c.green/65000.0,c.blue/65000.0,1)
  [c.red/65535.0,c.green/65535.0,c.blue/65535.0,opacity>1 ? 1 : opacity<0 ? 0 : opacity]
end

.html_color(str) ⇒ Object



242
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 242

def self.html_color(str)  ::Gdk::Color.parse(str.kind_of?(Array) ? str.last : str) end

Instance Method Details

#_Object



222
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 222

def _() @current_widget end

#_accept?(type) ⇒ Boolean

Returns:

  • (Boolean)


66
67
68
69
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 66

def _accept?(type)
  w=@lcur.last
  w.respond_to?(:accept?) ? w.accept?(type) : true
end

#_cbox(expand, box, config, add1) ⇒ Object

private: generic packer



125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 125

def _cbox(expand,box,config,add1)
  autoslot() # pack last widget before append new bow
  parent=@lcur.last
  if add1
   _pack(parent,box,expand)
  end
  _set_accepter(box,:layout,:widget)
  @lcur << box
  yield
  autoslot() # pack last widget before closing box
  @lcur.pop 
  apply_options(box,config) 
end

#_check_append(name, w, wref) ⇒ Object



102
103
104
105
106
107
108
109
# File 'lib/ruiby_gtk/dsl/commands.rb', line 102

def _check_append(name,w,wref)
  raise("#{name}(w,r) : Widget ref not created!") unless wref
  raise("#{name}(w,r) : new Widget not created!") unless w
  parent=wref.parent
  raise("#{name}(w,r): r=#{parent.inspect} is not a XBox or Frame !") unless !parent || parent.kind_of?(Container)
  raise("#{name}(w,r): r=#{parent.inspect} is not a XBox or Frame !") unless parent.respond_to?(:reorder_child)
  parent
end

#_create_log_windowObject



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/ruiby_gtk/dsl/commands.rb', line 198

def _create_log_window() 
  return(@loglabel) if defined?(@loglabel) && @loglabel && ! @loglabel.destroyed?
  wdlog = Dialog.new(
          title: "Logs #{$0}",
          parent: nil,
          flags: 0,
          buttons:   [[ :Validation,1]]
  )
  Ruiby.set_last_log_window(wdlog)
  logBuffer = TextBuffer.new
  @loglabel=TextView.new(logBuffer)
  @loglabel.override_font(  Pango::FontDescription.new("Courier new 10")) 
  sw=ScrolledWindow.new()
  sw.set_width_request(800) 
  sw.set_height_request(200)  
  sw.set_policy(:automatic, :always)
  
  sw.add_with_viewport(@loglabel)
  wdlog.child.pack_start(sw, :expand => true, :fill => true, :padding => 3)
  wdlog.signal_connect('response') { wdlog.destroy }
  wdlog.show_all  
  @loglabel
end

#_dyn_check_button(text, var, option = {}) ⇒ Object



89
90
91
92
93
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 89

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

#_dyn_entry(var, size, options, slotied) ⇒ Object



167
168
169
170
171
172
173
174
175
176
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 167

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

#_dyn_fslider(var, option, &blk) ⇒ Object



114
115
116
117
118
119
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 114

def _dyn_fslider(var,option,&blk) 
  w=  block_given? ?  fslider(var.value,option,&blk) : fslider(var.value,option) { |v| var.value=v }
  var.observ { |v| w.set_value(v) }
  attribs(w,option)   
  w
end

#_dyn_ientry(var, options, slotied) ⇒ Object



195
196
197
198
199
200
201
202
203
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 195

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

#_dyn_islider(var, option, &blk) ⇒ Object



265
266
267
268
269
270
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 265

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

#_dyn_label(var, option = {}) ⇒ Object



14
15
16
17
18
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 14

def _dyn_label(var,option={}) 
  w=  label(var.value.to_s,option) 
  var.observ { |v| w.text = v.to_s }
  w
end

#_dyn_progress(var, text, option) ⇒ Object



306
307
308
309
310
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 306

def _dyn_progress(var,text,option)
  w=progress(var.value,text,option)
  var.observ {|value| w.set_fraction(value.to_f)}
  w
end

#_dyn_toggle_button(text1, text2, var, option = {}, &blk) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 74

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

#_exe_posix(cmd, to) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ruiby_gtk/dsl/script.rb', line 93

def _exe_posix(cmd,to)
   Thread.new(cmd,to) do |cmd,to|
     begin
       PTY.spawn(cmd) do |read,write,pid|
         $ruiby_script_pid= pid
         ( read.each { |output| (log output.chomp) } if read ) rescue log $!.to_s
       end
       rescue Exception => e
         log "Exception #{e} #{"  "+e.backtrace.join("\n  ")}"
         Process.kill(9,$ruiby_script_pid) if $ruiby_script_pid
         $ruiby_script_pid= nil
       ensure
     end
     gui_invoke { log("done") }
   end
end

#_exe_windows(cmd, to) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/ruiby_gtk/dsl/script.rb', line 69

def _exe_windows(cmd,to)
  Thread.new(cmd,to) do |cmd,to|
  begin
    STDOUT.sync = true
    process=nil
    Open3.popen3(cmd) do |sin,sout,serr,process0|
      process=process0
      $ruiby_script_pid= process.pid
      sin.close_write
      loop do
        r,w,e=IO.select([sout,serr],nil,nil,to || 10)
        r.each { |a| log a.read.chomp} if r
        break unless r && !sout.eof? && !serr.eof?
      end
    end
    gui_invoke { log("done: status=#{process.value}") }
    $ruiby_script_pid=nil
  rescue Exception => e
  Process.kill(9,process.pid) if process
    log "Exception #{e} #{"  "+e.backtrace.join("\n  ")}"
    $ruiby_script_pid= nil
  end ; end
end

#_label(text, options = {}) ⇒ Object



31
32
33
34
35
36
37
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 31

def _label(text,options={})
  l=if text && text[0,1]=="#"
    get_image_from(text[1..-1],options[:isize]||:button);
  else
    Label.new(text);
  end
end

#_make_prop_line(prop_current, options, k, v) ⇒ Object



340
341
342
343
344
345
346
347
348
349
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 340

def _make_prop_line(prop_current,options,k,v)
  if k.to_s =~/^sep\d+$/
      cell_span(2,HSeparator.new)
  else
    cell_right(label(" "+k.to_s+" : "))
    cell_left(options[:edit] ? 
      (prop_current[k]=entry(v.to_s)) : 
      label(v.to_s))
  end
end

#_nocodeeeeeeeeeeeObject



47
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 47

def _nocodeeeeeeeeeee() end

#_pack(parent, box, expand) ⇒ Object



138
139
140
141
142
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 138

def _pack(parent,box,expand)
   parent.respond_to?(:pack_start) ? 
        parent.pack_start(box, :expand => expand, :fill => true): 
        parent.add(box) 
end

#_paned(horizontal, size, fragment) ⇒ Object



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 288

def _paned(horizontal,size,fragment)    
  s=stack {} # create a temporary container for inner widgets
  @lcur << s
  yield()
  autoslot
  @lcur.pop
  
  raise("panned : must contain only 2  children") if s.children.size!=2
  
  frame1,frame2=*s.children

  (frame1.shadow_type = :in)  rescue nil
  (frame2.shadow_type = :in)  rescue nil
  
  paned = Paned.new(horizontal ? :horizontal : :vertical)
  paned.position=size*fragment
  horizontal ? paned.set_size_request(size, -1) : paned.set_size_request(-1,size)
  
  s.remove(frame1)
  s.remove(frame2)
  s.parent.remove(s)
  paned.pack1(frame1, :resize => true, :shrink => false)
  paned.pack2(frame2, :resize => true, :shrink => false)
  slot(paned)
end

#_radio_buttons(sens, ltext = ["empty!"], value = -1,, &blk) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 119

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

#_set_accepter(layout, *types) ⇒ Object

add a accept?() method to a layout (box). so children will check if they can be added to current layout by invoke

current_layout=@lcur.last
current_layout.accept?( <type-children> )


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 51

def _set_accepter(layout,*types)
  if types.size==1
    layout.define_singleton_method(:accept?) do |type|  
       raise("No command   #{type} accepted here, accept=#{types}/") unless types.first==type 
    end
  elsif types.size==2
    layout.define_singleton_method(:accept?) do |type|  
      raise("No command #{type} accepted here, accept=#{types}/") unless types.first==type || types.last==type 
    end
  else
    layout.define_singleton_method(:accept?) do |type| 
      raise("No command  #{type}  accepted here, accept=#{types}/") unless types.any? { |a| a==type }
    end
  end
end

#_sub_image(name) ⇒ Object



128
129
130
# File 'lib/ruiby_gtk/dsl/pixmap.rb', line 128

def _sub_image(name)
  Image.new(pixbuf: get_pixbuf(name))
end

#aaa_generalitiesObject

All Ruiby commands correspond of

* a object creation (container, widget), see later,
* complement set propertie to current/alst widget created : css_name(), space(), tooltip()
* a immediate dialog (modal) command : 
<ul>
    alert  ask ask_color ask_dir_to_read ask_dir_to_write ask_file_to_read  
    ask_file_to_write color_choice dialog dialog_async edit 
 </ul>
* a immediate command, the can be used in callback code: gui manipulation... : 
<ul>
    after  anim append_to apply_options  attribs autoslot chrome clear clear_append_to  
    color_conversion def_style def_style3 delete  force_update get_config  
    get_current_container get_icon get_image_from get_pixbuf get_stockicon_pixbuf 
    gui_invoke gui_invoke_in_window gui_invoke_wait  hide_app  log  on_destroy  
    rposition ruiby_component ruiby_exit  show_methods 
    slot_append_after slot_append_before sloti style threader  update 
 </ul>

2 kinds of objects :

  • container <ul>

       accordion box  center flow flow_paned flowi frame framei  grid haccordion notebook  
       pclickable popup stack stack_paned stacki systray table var_box var_boxi vbox_scrolled 
    </ul>
    Containers organyze children widget, but show (almost) nothing.
    Children must be created in container bloc, container can contains widget and container :
       <code><pre> 
       | stack do
       |   button("Hello")
       |   label(" word")
       |   flow { button("x") ; label("y") }
       | end
       </pre></code>
  • widgets <ul>

       aitem alabel box button  calendar canvas cell* center check_button  combo  dialog
       dialog_async  edit entry fentry field fields grid  haccordion  hradio_buttons htoolbar ientry image  
       islider label labeli list menu menu_bar menu_button menu_checkbutton menu_separator   page  
       pp_item pp_separator  properties   row   scrolled separator  show_methods  source_editor space 
       syst_add_button syst_add_check syst_add_sepratator syst_icon syst_quit_button 
       systray table text_area  toggle_button  tree_grid  vradio_buttons wtree
    </ul>
    Widget must be placed in a container.
    2 kinds of placement :
    <li>sloted  : widget take all disponible space ( gtk: pack(expand,fill) ), share
                 space with other sloted widget in same container
    <li>slotied : widget take only necessary place ( gtk: pack(no-expand , no-fill) )

<pre><code>

|------------------------|
|<buttoni               >|
|<labeli                >|
|<--------------------- >|
|<                      >|
|<    button            >|
|<                      >|
|<--------------------- >|
|<                      >|
|<    label             >|
|<                      >|
|<--------------------- >|
|<buttoni               >|
|------------------------|

</code></pre>

by default, all widgetcontainer are sloted !
widget name ended by 'i' ( buttoni, labeli, stacki , flowi ...) are slotied

slot()  command is deprecated. sloti() command must be use if *i command 
do not exist  :  w=sloti( widgetname() {...} )
space() can be used for slot a empty space.

Attachement to a side of a container is not supported. You must put empty sloted widget in free space: <li> scoth xxxx in top of frame : >stack { stacki { xxx } ; stack { } } <li> scoth xxxx in bottom of frame : >stack { stack { } ; stacki { xxx } } <li> scoth xxxx in left of frame : >flow { flowi { xxx } ; stack { } }

Dynamique variables bindings The class ::DynVar support a single value and the observer pattern. So widgets can be associate with an dynamique value :

  • if the value change, the widget change the view accordingly to the new value,

  • if the view change by operator action, the value change accordingly.

  • Threading is care : widget updates will be done in maint thread context

Widgets which supports DynVar are :

  • entry,ientry,

  • label,

  • islider,

  • check_button

This list will be extende to combo_button, toggle_button, list, grid …

'make_DynClass' and 'make_StockDynClass' can be use for creation of Class/object which contain DynVar : as OStruct, but data members are DynVar.

<li>@calc=make_DynObject({“resultat”=> 0,“value” => “0” , “stack” => [] }) <li>@calc=make_StockDynObject(“name”,{“resultat”=> 0,“value” => “0” , “stack” => [] }) create a object, name him for Stock, give default values if object does not exists in current stock.



155
156
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 155

def aaa_generalities()
end

#accordionObject

create a accordion menu. must contain aitem() which must containe alabel() : accordion { aitem(txt) { alabel(lib) { code }; …} … }



231
232
233
234
235
236
237
238
239
240
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 231

def accordion() 
  _accept?(:layout)
  @slot_accordion_active=nil #only one accordion active by window!
  w=stack { stacki {
    _set_accepter(@lcur.last,:aitem,:layout,:widget)
    yield
  }}
  separator
  w
end

#aitem(txt, &blk) ⇒ Object

a button menu in accordion

bloc is evaluate for create/view a list of alabel :
aitem(txt) { alabel(lib) { code }; ...}


257
258
259
260
261
262
263
264
265
266
267
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 257

def aitem(txt,&blk) 
  _accept?(:aitem)
  b2=Gtk::Box.new(:vertical,2)
  _set_accepter(b2,:alabel,:layout,:widget)
  b=button(txt) {
        clear_append_to(@slot_accordion_active) {} if @slot_accordion_active
        @slot_accordion_active=b2
        clear_append_to(b2) {  blk.call() }
        slot_append_after(b2,b)
  }
end

#alabel(txt, &blk) ⇒ Object

create a button-entry in a accordion menu bloc is evaluate on user click. must be in aitem() bloc : accordion { aitem(txt) { alabel(lib) { code }; …} … }



272
273
274
275
276
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 272

def alabel(txt,&blk)
  _accept?(:alabel)
  l=nil
  pclickable(proc { blk.call(l) if blk} ) { l=label(txt) }
end

#append_to(cont, &blk) ⇒ Object

append the result of bloc parameter to a contener (stack or flow) thread protected Usage :

@stack= stack {}
  . . . . 
  append_to(@stack) { button("Hello") }


12
13
14
15
16
17
18
19
20
21
22
# File 'lib/ruiby_gtk/dsl/commands.rb', line 12

def append_to(cont,&blk)
  if $__mainthread__ != Thread.current
    gui_invoke { append_to(cont,&blk) }
    return
  end
  @lcur << cont
  yield 
  autoslot()
  @lcur.pop
  show_all_children(cont)
end

#apply_options(w, options) ⇒ Object

apply some styles property to an existing widget. options are :size, :width; :height, :margins, :bg, :fg, :font apply_options(w,

:size=> [10,10], 
:width=>100, :heigh=>200,
:margins=> 10
:bg=>'#FF00AA",
:fg=> Gdk::Color:RED,
:tooltip=> "Hello...",
:font=> "Tahoma bold 32"

)



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 207

def apply_options(w,options)
    w.set_size_request(*options[:size])                                 if options[:size] 
    w.set_border_width(options[:margins])                               if options[:margins]  
    w.width_request=(options[:width].to_i)                              if options[:width]
    w.height_request=(options[:height].to_i)                            if options[:height]
    w.override_background_color(:normal,color_conversion(options[:bg])) if options[:bg] 
    w.override_color(:normal,color_conversion(options[:fg]))            if options[:fg] 
    w.override_font(Pango::FontDescription.new(options[:font]))         if options[:font]
 w.set_name(options[:id]) if options[:id]
    if options[:tooltip]
      w.set_tooltip_markup(options[:tooltip])
      w.has_tooltip=true
    end
    w
end

#attribs(w, options) ⇒ Object

common widget property applied for (almost) all widget. options are last argument of every dsl command, see apply_options



188
189
190
191
192
193
194
195
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 188

def attribs(w,options)
    _accept?(:widget) 
    #p options if options && options.size>0
    apply_options(w,options)
    autoslot(w)  # slot() precedent widget if exist and not already sloted, and declare this one as the precedent
    def w.options(config) $__mainwindow__.apply_options(self,config) end
    w
end

#autoslot(w = nil) ⇒ Object

slot() precedently created widget if not sloted. this is done by attribs(w) which is call after construction of almost all widget



153
154
155
156
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 153

def autoslot(w=nil)
  (slot(@current_widget)) if @current_widget!=nil
  @current_widget=w 
end

#background(color, options = {}, &b) ⇒ Object

set a background color to current container Usage : stack { background(“#FF0000”) { flow { …} } }



162
163
164
165
166
167
168
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 162

def background(color,options={},&b)
  _accept?(:layout)
  eventbox = Gtk::EventBox.new
  ret=_cbox(true,eventbox,{},true,&b)
  apply_options(eventbox,{bg: color}.merge(options))
  ret
end

#backgroundi(color, options = {}, &b) ⇒ Object



169
170
171
172
173
174
175
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 169

def backgroundi(color,options={},&b)
  _accept?(:layout)
  eventbox = Gtk::EventBox.new
  ret=_cbox(false,eventbox,{},true,&b)
  apply_options(eventbox,{bg: color}.merge(options))
  ret
end

#bourrage(n = 1) ⇒ Object



56
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 56

def bourrage(n=1) n.times { label(" ") }  end

#box(sens = :vertical) ⇒ Object

box { } container which manage children widget without slot (pack()) in parent container. Use it for cell in table, notebook : table { row { cell(box { });… }; … }



29
30
31
32
33
34
35
36
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 29

def box(sens=:vertical) 
  box=Gtk::Box.new(sens,2)
  _set_accepter(box,:layout,:widget)
  @lcur << box
  yield
  autoslot()
  @lcur.pop
end

#button(text, option = {}, &blk) ⇒ Object

create button, with text (or image if txt start with a '#') block argument is evaluate at button click



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 60

def button(text,option={},&blk)
  if text && text[0,1]=="#"
    b=Button.new(:label => nil, :mnemonic => nil, :stock_id => nil);
    text,tooltip=text[1..-1].split("//")
    b.set_image( get_image(text) )
    if tooltip
      b.has_tooltip= true
      b.tooltip_text=tooltip  
    end
  else
    b=Button.new(:label => text, :mnemonic => nil, :stock_id => nil);
  end
  b.signal_connect("clicked") { |e| blk.call(e) rescue error($!) } if blk
  apply_options(b.child,option)
  attribs(b,option)
end

#button_expand(text, initiale_state = false, options = {}, &b) ⇒ Object

a button which show a sub-frame on action



315
316
317
318
319
320
321
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 315

def button_expand(text,initiale_state=false,options={},&b) 
  expander = Gtk::Expander.new(text)
  expander.expanded = initiale_state
  frame=box(&b)
  expander.add(frame)
  attribs(expander,options)
end

#button_icon_text(icon, text = "", options = {}, &b) ⇒ Object

a button with icon+text verticaly aligned, can be call anywhere, and in htool_bar_with_icon_text option is label options and isize ( option for icon size, see label())



304
305
306
307
308
309
310
311
312
313
314
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 304

def button_icon_text(icon,text="",options={},&b)
     if icon !~ /^sep/
        spacei
        pclickablie(proc { b.call  }) { stacki { 
            label("#"+icon,{isize: (options[:isize] || :dialog) }) 
            label(text[0,15],options)
       } }
     else
        separator
     end
end

#button_left_icon_text(icon, text = "", options = {}, &b) ⇒ Object



315
316
317
318
319
320
321
322
323
324
325
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 315

def button_left_icon_text(icon,text="",options={},&b)
     if icon !~ /^sep/
        spacei
        pclickablie(proc { b.call  }) { flowi { 
            label("#"+icon,{isize: (options[:isize] || :dialog) }) 
            label(text,options)
       } }
     else
        separator
     end
end

#buttoni(text, option = {}, &blk) ⇒ Object

create button, with text (or image if txt start with a '#') block argument is evaluate at button click, slotied :

packed without expand for share free place


80
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 80

def buttoni(text,option={},&blk) sloti(button(text,option,&blk)) end

#calendar(time = Time.now, options = {}) ⇒ Object

Month Calendar with callback on month/year move and day selection : calendar(Time.now-24*3600, :selection => proc {|day| } , :changed => proc {|widget| } calendar respond to

  • set_time(time) ; set a selected date from a Time object

  • get_time() ; return Time of selected day



427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 427

def calendar(time=Time.now,options={})
  c = Calendar.new
  #c.display_options(Calendar::SHOW_HEADING | Calendar::SHOW_DAY_NAMES |  
  #        Calendar::SHOW_WEEK_NUMBERS )
  after(1) { c.signal_connect("day-selected") { |w,e| options[:selection].call(w.day)  rescue error($!) } } if options[:selection]
  after(1) { c.signal_connect("month-changed") { |w,e| options[:changed].call(w)  rescue error($!) } }if options[:changed]
  class << c
  ;  def set_time(time)
      select_month(time.month,time.year)
      select_day(time.day)
    end
  ;  def get_time()
      year, month, day= *date() 
      Time.local(year, month, day) 
    end
  end
  c.set_time(time)
  attribs(c,options)
end

#canvas(width, height, option = {}) ⇒ Object

Create a drawing area, for pixel/vectoriel draw for interactive actions see test.rb fo little example.

@cv=canvas(width,height,opt) do

on_canvas_draw { |w,ctx|  myDraw(w,ctx) }
on_canvas_button_press {|w,e|  [e.x,e.y]  } # must return a object which will given to next move/release callback
on_canvas_button_motion {|w,e,o| n=[e.x,e.y] ; ... ; n }
on_canvas_button_release {|w,e,o| ... }
on_canvas_keypress       {|w,key| ... }

end

for drawing in canvas, this commands are usable. basic gtk commands can still be uses ( move_to(), line_to()… ) def myDraw(w,ctx)

w.init_ctx(color_fg="#000000",color_bg="#FFFFFF",width=1)
w.draw_point(x1,y1,color,width)
w.draw_polygon([x,y,...],colorFill,colorStroke,widthStroke)
w.draw_circle(cx,cy,rayon,colorFill,colorStroke,widthStroke)
w.draw_rectangle(x0,y0,w,h, r,widthStroke,colorFill,w)
cv.draw_rounded_rectangle(x0,y0,w,h,ar,colorStroke,colorFill,widthStroke)
w.draw_pie(x,y,r,l_ratio_color_label)
w.draw_arc(x,y,r,start,eend,width,color_stroke,color_fill=nil) # camenber
w.draw_arc2(x,y,r,start,eend,width,color_stroke,color_fill=nil) # circle fraction
w.draw_varbarr(x0,y0,x1,y1,vmin,vmax,l_date_value,width) {|value| color}
w.draw_image(x,y,filename,sx,sy)
cv.ctx_font(name,size)        # choose font name and size for next draw_text...
w.draw_text(x,y,text,scale,color,bgcolor=nil)
w.draw_text_left(x,y,text,scale,color,bgcolor=nil)
w.draw_text_center(x,y,text,scale,color,bgcolor=nil)
lxy=w.translate(lxy,dx=0,dy=0) # move a list of points
lxy=w.rotate(lxy,x0,y0,angle)  # rotate a list of points
cv.rotation(cx,cy,a) { draw... }
w.scale(10,20,2) { w.draw_image(3,0,filename) } 
              >> draw in a transladed/scaled coord system
              >> image will be draw at 16/20 (10+3*2)/(20+0*2)
                 , and size doubled

end gradient can be use for recangle and polygone, see samples/gradients.rb in place od String bg-color, say Array : #wdirection color1 color2 … type = linear/radial direction : tb Top->Bottom, bu Bottom->Up , lr: Left->Right, …, trb -> TopLeft -> BottomRight



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/ruiby_gtk/dsl/canvas.rb', line 48

def canvas(width,height,option={})
  autoslot()
  cv=DrawingArea.new()
  cv.width_request=width
  cv.height_request=height
  cv.add_events(Gdk::EventMask::BUTTON_PRESS_MASK  | Gdk::EventMask::BUTTON_MOTION_MASK | Gdk::EventMask::KEY_PRESS_MASK)
  cv.can_focus = true
  @currentCanvas=cv
  @lcur << HandlerContainer.new
  yield
  @lcur.pop
  @currentCanvas=nil

  attribs(cv,option) 
  def cv.set_memo(memo) @memo=memo end
  def cv.get_memo() @memo end
  cv.set_memo(nil)    
  
  def cv.redraw() 
    self.queue_draw_area(0,0,self.allocation.width,self.allocation.height)
  end
  def cv.app_window()  @app_window end
  def cv.set_window(r) @app_window=r end
  cv.set_window(self)
  def cv.init_ctx(color_fg="#000000",color_bg="#FFFFFF",width=1)
      w,cr=*@currentCanvasCtx 
      cr.set_line_join(Cairo::LINE_JOIN_ROUND)
      cr.set_line_cap(Cairo::LINE_CAP_ROUND)
      cr.set_line_width(width)
      cr.set_source_rgba(*Ruiby_dsl.cv_color_html(color_bg))
      cr.paint
      #cr.set_source_rgba(*Ruiby_dsl.cv_color_html(color_fg))
      @currentWidth=width
      @currentColorFg=color_fg
      @currentColorBg=color_bg
  end
  
  def cv.draw_line(lxy,color=nil,width=nil) 
      raise("odd number of coord for lxy") if !lxy || lxy.size==0 || lxy.size%2==1
      if lxy.size==2 
        return draw_point(lxy.first,lxy.last,color,width)
      end
      _draw_poly(lxy,color|| @currentColorFg,nil,width)
  end
  def cv.draw_polygon(lxy,colorStroke=nil,colorFill=nil,widthStroke=nil)
      raise("odd number of coord for lxy") if !lxy || lxy.size==0 || lxy.size%2==1
      if lxy.size==2 
        return draw_point(lxy.first,lxy.last,colorStroke,widthStroke)
      end
     colorStroke=@currentColorFg if colorFill.nil? && colorStroke.nil?
      _draw_poly(lxy,colorStroke,colorFill,widthStroke)
  end
  def cv._set_gradient(cv,cr,acolor,lxy)
    type,sens,*data=acolor
    return unless type && sens && data && data.size>=2
    cr.set_source_rgba(*Ruiby_dsl.cv_color_html("#FFF"))
    if type =~ /^g/
      x0,y0,x1,y1=*bbox(lxy)
      case sens
        when "tb" then x0,y0,x1,y1 = x1/2,y0,  x1/2,y1
        when "bu" then x1,y1,x0,y0 = x1/2,y0,  x1/2,y1
        when "lr" then x0,y0,x1,y1 = 0,y1/2,   x1,y1/2
        when "rl" then x0,y0,x1,y1 = x1,y1/2,  x0,y1/2
        when "trb" then x0,y0,x1,y1 = x0,y0,   x1,y1
        when "tlb" then x0,y0,x1,y1 = x1,y0,   x0,y1
        else
          error("unknown gradient : #{sens}")
      end
      #p [sens,x0,y0,x1,y1]
      pattern = Cairo::LinearPattern.new(x0,y0,x1,y1)
      last_color="#000"
      data.each_with_index {|color,i|
          pos= 1.0*i/(data.length-1)
          #p [pos,Ruiby_dsl.cv_color_html(color)]
          color=last_color if color=="-"
          last_color=color
          pattern.add_color_stop(pos, *(Ruiby_dsl.cv_color_html(color)[0..2]))
       }
       cr.set_source(pattern)
    else
    end
  end
  def cv.bbox(lxy)
    xmin,ymin=lxy[0..1]
    xmax,ymax=lxy[0..1]
    lxy.each_slice(2) {|x,y| 
      xmin=x if x<xmin ;ymin=y if y<ymin 
      xmax=x if x>xmax ;ymax=y if y>ymax 
    }
    [xmin,ymin,xmax,ymax]
  end
  def cv._draw_poly(lxy,color_fg,color_bg,width)
      raise("odd number of coord for lxy") if !lxy || lxy.size==0 || lxy.size%2==1
      w,cr=@currentCanvasCtx
      cr.set_line_width(width) if width
      x0,y0,*poly=*lxy
      if color_bg
        cr.move_to(x0,y0)
        poly.each_slice(2) {|x,y| cr.line_to(x,y) } 
        if Array === color_bg
          _set_gradient(w,cr,color_bg,lxy)
        else
           cr.set_source_rgba(*Ruiby_dsl.cv_color_html(color_bg))
        end
        cr.fill
      end
      if color_fg
        #p lxy
        cr.set_source_rgba(*Ruiby_dsl.cv_color_html(color_fg))
        cr.move_to(x0.round,y0.round)
        poly.each_slice(2) {|x,y| cr.line_to(x.round,y.round) } 
        cr.stroke()  
      end
  end
  def cv.draw_point(x,y,color=nil,width=nil)
    width||=@currentWidth
    draw_line([x,y-width/2.0, x,y+width/2.0],color,width)
  end
  def cv.draw_varbarr(x0,y0,x1,y1,dmin,dmax,lvalues0,width,&b)
    ax=1.0*(x1-x0)/(dmax-dmin) ;bx= x0-ax*dmin 
    ay=1.0*(y1-y0)/(dmax-dmin) ;by= y0-ay*dmin 
    xconv=proc {|d| (x1==x0) ? x1 :  (ax*d+bx) }
    yconv=proc {|d| (y1==y0) ? y1 :  (ay*d+by) }
    w,cr=@currentCanvasCtx
    lvalues=lvalues0.sort_by {|a| a.first}
    l=[lvalues.first]+lvalues.each_cons(2).map {|(d,v),(d1,v1)|
      (v1 && v!=v1) ? [d1,v1] : nil 
    }.compact+[lvalues.last]
    #p l
    cr.set_line_join(Cairo::LINE_JOIN_MITER)
    cr.set_line_cap(Cairo::LINE_CAP_BUTT)
    l.each_cons(2).map {|(d,v),(d1,v1)| 
      next unless v1
      color=  block_given? ? yield(v) : v.to_s
      lxy=[xconv.call(d),yconv.call(d),xconv.call(d1),yconv.call(d1)]
      #p "   #{d},#{d1} ==> #{lxy.inspect}" if l.size>1
      w.draw_line(lxy,color, width) if color
    }
  end 
  def cv.ctx_font(name,size)
    w,cr=@currentCanvasCtx
    fd=Pango::FontDescription.new(name)
    cr.select_font_face(fd.family, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL)
    cr.set_font_size(size)
  end
  def cv.draw_text(x,y,text,scale=1,color=nil,bgcolor=nil)
    w,cr=@currentCanvasCtx
    cr.set_line_width(1)
    cr.set_source_rgba(*Ruiby_dsl.cv_color_html(color || @currentColorFg ))
    scale(x,y,scale) {  
      if bgcolor
        a=cr.text_extents(text)
        w.draw_rectangle(0,1,a.width,-a.height,1,bgcolor,bgcolor,0)
        cr.set_source_rgba(*Ruiby_dsl.cv_color_html(color || @currentColorFg ))
      end
      cr.move_to(0,0)
      cr.show_text(text) 
      cr.fill
    }
  end
  
  def cv.draw_text_left(x,y,text,scale=1,color=nil,bgcolor=nil)
    w,cr=@currentCanvasCtx
    cr.set_line_width(1)
    cr.set_source_rgba(*Ruiby_dsl.cv_color_html(color || @currentColorFg ))
    scale(x,y,scale) {  
      a=cr.text_extents(text)
      if bgcolor
        w.draw_rectangle(-a.width,-a.height,a.width,a.height+2,1,bgcolor,bgcolor,1)
        cr.set_source_rgba(*Ruiby_dsl.cv_color_html(color || @currentColorFg ))
      end
      cr.move_to(-a.width,0)
      cr.show_text(text) 
      cr.fill
    }
  end
  def cv.draw_text_center(x,y,text,scale=1,color=nil,bgcolor=nil)
    w,cr=@currentCanvasCtx
    cr.set_line_width(1)
    cr.set_source_rgba(*Ruiby_dsl.cv_color_html(color || @currentColorFg ))
    scale(x,y,scale) {  
      a=cr.text_extents(text)
      if bgcolor
        w.draw_rectangle(-a.width/2,-1,a.width,-a.height-3,1,bgcolor,bgcolor,1)
        cr.set_source_rgba(*Ruiby_dsl.cv_color_html(color || @currentColorFg ))
      end
      cr.move_to(-a.width/2.0,0)
      cr.show_text(text) 
      cr.fill
    }
  end
  def cv.draw_rectangle(x0,y0,w,h,r=0,colorStroke=nil,colorFill=nil,widthStroke=nil)
    return draw_rounded_rectangle(x0,y0,w,h,r,colorStroke,colorFill,widthStroke) if r.kind_of?(Array) || r>1
    x1, y1 = x0+w, y0+h
    colorStroke=@currentColorFg if colorFill.nil? && colorStroke.nil?
    _draw_poly([x0,y0, x1,y0, x1,y1, x0,y1, x0,y0],colorStroke,colorFill,widthStroke)
  end
  def cv.draw_rounded_rectangle(x0,y0,w,h,ar,colorStroke,colorFill,widthStroke)
    cv,cr=@currentCanvasCtx
    pi=Math::PI
    ar=[ar,ar,ar,ar] if ar.kind_of?(Numeric)
    if Array === colorFill
      _set_gradient(w,cr,colorFill,[x0,y0,x0+w,y0+h])
    else
      cr.set_source_rgba(*Ruiby_dsl.cv_color_html(colorFill ? colorFill : colorStroke))
    end
    cr.set_line_width( widthStroke )
    r=ar[0]
    cr.move_to(x0,y0+r)
    cr.arc(x0+r,y0+r, r, -pi,-pi/2)
    r=ar[1]
    cr.line_to(x0+w-r,y0)
    cr.arc(x0+w-r,y0+r, r, -pi/2, 0)
    r=ar[2]
    cr.line_to(x0+w,y0+h-r)
    cr.arc(x0+w-r,y0+h-r, r, 0, pi/2)
    r=ar[3]
    cr.line_to(x0+r,y0+h)
    cr.arc(x0+r,y0+h-r, r, pi/2,pi)
    r=ar[0]
    cr.line_to(x0,y0+r)
    colorFill ? cr.fill : cr.stroke
  end
  def cv.draw_circle(x0,y0,r,color_bg=nil,color_fg=nil,width=nil)
      w,cr=@currentCanvasCtx
      cr.set_line_width(width || @currentWidth )
      if color_bg
        color=Ruiby_dsl.html_color(color_bg)
        cr.set_source_rgba(color.red/65000.0, color.green/65000.0, color.blue/65000.0, 1)
        cr.arc(x0,y0, r, width || @currentWidth , 3.0*Math::PI)
        cr.fill
      end
      if color_fg
        color=Ruiby_dsl.html_color(color_fg)
        cr.set_source_rgba(color.red/65000.0, color.green/65000.0, color.blue/65000.0, 1)
        cr.arc(x0,y0, r, 0 , 3.0*Math::PI)
        cr.stroke
      end
  end
  def cv.draw_pie(x0,y0,r,l_ratio_color_txt,with_legend=false)
    lcolor=%w{#F00 #A00 #AA0 #AF0 #AAF #AAA #FAF #AFA #33F #044}
    cv,ctx=@currentCanvasCtx
    start=3.0*Math::PI/2.0
    total=l_ratio_color_txt.inject(0.0) { |sum,(a)| sum+a }
    h0=y0-r
    p0=x0+r*1.2
    l_ratio_color_txt.each_with_index { |(sweep0,coul,text),i|
      coul=lcolor[i%lcolor.size] unless coul
      sweep=sweep0/total
      eend=start+Math::PI*2.0*sweep
      mid=(eend+3*start)*0.25
      if !with_legend && sweep>0.11 && r>=20 && text && text.size>0
        dx=(mid>(Math::PI/2+Math::PI/4) && mid < Math::PI*3/2) ? text.size*7 : 0
        cv.draw_line([x0,y0,x0+1.4*r*Math.cos(mid),y0+1.4*r*Math.sin(mid)],"#000",1)
        cv.draw_text(x0+1.4*r*Math.cos(mid)-dx,y0+1.4*r*Math.sin(mid),text,1,coul)
      end
      if with_legend && text && text.size>0 && h0<y0+r
        draw_rectangle(p0,h0,20,8,0,"#000",coul,1)
        draw_text(p0+30,h0+10,text,1,"#000")
        draw_text(p0+70,h0+10,": #{sweep0}",1,"#000")
        h0+=10
      end
      ctx.move_to(x0,y0)
      ctx.line_to(x0+r*Math.cos(start),y0+r*Math.sin(start)) 	
      ctx.arc( x0,y0, r, start, eend );
      ctx.close_path
      ctx.set_line_width( 1.0 )
      ctx.set_source_rgba(*Ruiby_dsl.cv_color_html(coul))
      ctx.fill
      #ctx.set_source_rgba(*Ruiby_dsl.cv_color_html("#000"))
      #ctx.stroke
      start=eend
    }
  end
  def cv.draw_arc2(x,y,r,start,eend,width,color_stroke,color_fill=nil)
    w,ctx=@currentCanvasCtx
    ctx.set_line_width( width )
    ctx.set_source_rgba(*Ruiby_dsl.cv_color_html(color_fill ? color_fill : color_stroke))
    ctx.arc( x,y, r, Math::PI*2.0*start, Math::PI*2.0*eend );
    color_fill ? ctx.fill : ctx.stroke
  end
  def cv.draw_arc(x,y,r,start,eend,width,color_stroke,color_fill=nil)
    w,ctx=@currentCanvasCtx
    ctx.set_line_width( width )
    ctx.set_source_rgba(*Ruiby_dsl.cv_color_html(color_fill ? color_fill : color_stroke))
    ctx.move_to(x,y)
    ctx.arc( x,y, r, Math::PI*2.0*start, Math::PI*2.0*eend );
    ctx.close_path
    color_fill ? ctx.fill : ctx.stroke
  end
  def cv.draw_image(x,y,filename,sx=1,sy=sx)
    w,cr=@currentCanvasCtx
    pxb=w.app_window.get_pixbuf(filename)
    scale(x,y,sx,sy) { cr.set_source_pixbuf(pxb,0,0) ; cr.paint}
    [pxb.width,pxb.height]
  end
  # draw in scale factor
  # scale(20,100,2,4) { w.draw_line(10,10,20,20) ; ... } 
  # the line will be scaling by x/y factor 2 and 4 relative to center
  # point x=10 y=100
  def cv.scale(cx,cy,ax,ay=nil,&blk)
   ay=ax unless ay
   w,cr=@currentCanvasCtx
   cr.translate(cx,cy)
   cr.scale(ax,ay)
   blk.call rescue Message.error $!
   cr.scale(1.0/ax,1.0/ay)
   cr.translate(-cx,-cy)
  end
  def cv.rotation(cx,cy,a,&blk) 
   w,cr=@currentCanvasCtx
   cr.translate(cx,cy)
   cr.rotate(Math::PI*2.0*a)
   blk.call rescue Message.error $!
   cr.rotate(-Math::PI*2.0*a)
   cr.translate(-cx,-cy)
  end
  def cv.translate(lxy,dx=0,dy=0) 
    lxy.each_slice(2).inject([]) {|l,(x,y)| l <<x+dx; l <<y+dy}
  end
  def cv.rotate(lxy,x0,y0,angle)
    sa,ca=Math.sin(angle),Math.cos(angle)
    lxy.each_slice(2).inject([]) {|l,(x,y)| l << ((x-x0)*ca-(y-y0)*sa)+x0 ; l << ((x-x0)*sa+(y-y0)*ca)+y0}
  end
  cv
end

#canvasOld(width, height, option = {}) ⇒ Object

DEPRECATED; Create a drawing area, for pixel draw option can define closure :mouse_down :mouse_up :mouse_move for interactive actions



452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'lib/ruiby_gtk/dsl/canvas.rb', line 452

def canvasOld(width,height,option={})
  puts "*** DEPRECATED: use canvas do end in place of canvasOld ***"
  autoslot()
  w=DrawingArea.new()
  w.width_request=width
  w.height_request=height
  w.events |=  ( ::Gdk::EventMask::BUTTON_PRESS_MASK | ::Gdk::EventMask::POINTER_MOTION_MASK | ::Gdk::EventMask::BUTTON_RELEASE_MASK)

  w.signal_connect(  'draw' ) { |w1,cr| 
    cr.save {
      cr.set_line_join(Cairo::LINE_JOIN_ROUND)
      cr.set_line_cap(Cairo::LINE_CAP_ROUND)
      cr.set_line_width(2)
      cr.set_source_rgba(1,1,1,1)
      cr.paint
      if option[:expose]
        begin
          option[:expose].call(w1,cr) 
        rescue Exception => e
         bloc=option[:expose]
         option.delete(:expose)
         after(1) { error(e) }
         after(3000) {  puts "reset expose bloc" ;option[:expose] = nil }
        end  
      end
    }
  }
  @do=nil
  w.signal_connect('button_press_event')   { |wi,e| @do = option[:mouse_down].call(wi,e)  rescue error($!)              ; force_update(wi) }  if option[:mouse_down]
  w.signal_connect('button_release_event') { |wi,e| (option[:mouse_up].call(wi,e,@do)  rescue error($!)) if @do ; @do=nil ; force_update(wi) if @do }  if option[:mouse_up]
  w.signal_connect('motion_notify_event')  { |wi,e| (@do = option[:mouse_move].call(wi,e,@do) rescue error($!)) if @do     ; force_update(wi) if @do }  if option[:mouse_move]
  w.signal_connect('key_press_event')  { |wi,e| (option[:key_press].call(wi,e) rescue error($!)) ; force_update(wi) }  if option[:key_press]
  attribs(w,option) 
  def w.redraw() 
    self.queue_draw_area(0,0,self.width_request,self.height_request)
  end
  w
end

#cell(w) ⇒ Object

a cell in a row/table. take all space, centered



43
# File 'lib/ruiby_gtk/dsl/table.rb', line 43

def  cell(w)  cell_hspan(1,w)   end

#cell_bottom(w) ⇒ Object

create a cell in a row/table, bottom aligned



77
# File 'lib/ruiby_gtk/dsl/table.rb', line 77

def cell_bottom(w)   razslot();w.set_alignment(0.5, 1.0)rescue nil ; cell(w) end

#cell_hspan(n, w) ⇒ Object

a cell in a row/table. take space of n cells, horizontaly



45
# File 'lib/ruiby_gtk/dsl/table.rb', line 45

def  cell_hspan(n,w) cell_hvspan(n,0,w) end

#cell_hspan_left(n, w) ⇒ Object

create a hspan_cell in a row/table, left justified



70
# File 'lib/ruiby_gtk/dsl/table.rb', line 70

def cell_hspan_left(n,w)   razslot();w.set_alignment(0.0, 0.5)rescue nil ; cell_hspan(n,w) end

#cell_hspan_right(n, w) ⇒ Object

create a hspan_cell in a row/table, right justified



72
# File 'lib/ruiby_gtk/dsl/table.rb', line 72

def cell_hspan_right(n,w)  razslot();w.set_alignment(1.0, 0.5)rescue nil ; cell_hspan(n,w) end

#cell_hvspan(n, m, w) ⇒ Object

a cell in a row/table. take space of n x m cells, horizontaly x verticaly



49
50
51
52
53
54
55
56
57
58
# File 'lib/ruiby_gtk/dsl/table.rb', line 49

def  cell_hvspan(n,m,w) 
  _accept?(:cell)
  razslot();
  @lcur.last.attach(w,
     @ltable.last[:col],@ltable.last[:col]+n,
     @ltable.last[:row],@ltable.last[:row]+m+1
  )  
  @ltable.last[:col]+=n
  @ltable.last[:row]+=m
end

#cell_left(w) ⇒ Object

create a cell in a row/table, left justified



65
# File 'lib/ruiby_gtk/dsl/table.rb', line 65

def cell_left(w)     razslot();w.set_alignment(0.0, 0.5) rescue nil; cell(w) end

#cell_pass(n = 1) ⇒ Object

keep empty n cell consecutive on current row



60
# File 'lib/ruiby_gtk/dsl/table.rb', line 60

def  cell_pass(n=1)  @ltable.last[:col]+=n end

#cell_right(w) ⇒ Object

create a cell in a row/table, right justified



67
# File 'lib/ruiby_gtk/dsl/table.rb', line 67

def cell_right(w)    razslot();w.set_alignment(1.0, 0.5)rescue nil ; cell(w) end

#cell_span(n = 2, w) ⇒ Object

a cell in a row/table. take space of n cells, horizontaly



62
# File 'lib/ruiby_gtk/dsl/table.rb', line 62

def  cell_span(n=2,w) cell_hspan(n,w) end

#cell_top(w) ⇒ Object

create a cell in a row/table, top aligned



75
# File 'lib/ruiby_gtk/dsl/table.rb', line 75

def cell_top(w)      razslot();w.set_alignment(0.5, 0.0)rescue nil ; cell(w) end

#cell_vspan(n, w) ⇒ Object

a cell in a row/table. take space of n cells, verticaly



47
# File 'lib/ruiby_gtk/dsl/table.rb', line 47

def  cell_vspan(n,w) cell_hvspan(0,n,w) end

#cell_vspan_bottom(n, w) ⇒ Object

a cell_vspan aligned on bottom



81
# File 'lib/ruiby_gtk/dsl/table.rb', line 81

def cell_vspan_bottom(n,w) razslot();w.set_alignment(0.5, 1.0)rescue nil ; cell_vspan(n,w) end

#cell_vspan_top(n, w) ⇒ Object

a cell_vspan aligned on top



79
# File 'lib/ruiby_gtk/dsl/table.rb', line 79

def cell_vspan_top(n,w)    razslot();w.set_alignment(0.5, 0.0)rescue nil ; cell_vspan(n,w) end

#centerObject

center { } container which center his content (auto-sloted) TODO : tested!



84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 84

def center() 
  autoslot()
  valign = Gtk::Alignment.new(0,0,0,0)
  @lcur.last.pack_start(valign, :expand => true, :fill => false, :padding => 0)
  vbox=Box.new(:vertical, 0)
  _set_accepter(vbox,:layout,:widget)
  valign.add(vbox)
  @lcur << vbox
  yield
  autoslot()
  @lcur.pop
end

#check_button(text = "", value = false, option = {}, &blk) ⇒ Object

create a checked button state can be read by cb.active?



97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 97

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

#clear(cont) ⇒ Object

clear a containet (stack or flow) thread protected



26
27
28
29
30
31
32
33
# File 'lib/ruiby_gtk/dsl/commands.rb', line 26

def clear(cont) 
  if $__mainthread__ != Thread.current
    p "not in main thread"
    gui_invoke { clear(cont) }
    return
  end
  cont.children.each { |w| cont.remove(w) } 
end

#clear_append_to(cont, &blk) ⇒ Object

clear a container (stack or flow) and append the result of bloc parameter to this container thread protected



38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/ruiby_gtk/dsl/commands.rb', line 38

def clear_append_to(cont,&blk) 
  if $__mainthread__ != Thread.current
    p "not in main thread"
    gui_invoke { clear_append_to(cont,&blk) }
    return
  end
  cont.children.each { |w| cont.remove(w) } 
  @lcur << cont
  yield 
  autoslot()
  @lcur.pop
  show_all_children(cont)
end

#clickable(method_name, &b) ⇒ Object

specific to gtk : some widget like label can't support click event, so they must be contained in a clickable parent (EventBox)

Exemple: clickable(:callback_click_name) { label(“ click me! ”) }

click callback is definied by a method name. see pclickable for callback by closure.



141
142
143
144
145
146
147
148
149
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 141

def clickable(method_name,&b) 
  _accept?(:layout)
  eventbox = Gtk::EventBox.new
  eventbox.events =Gdk::EventMask::BUTTON_PRESS_MASK
  ret=_cbox(true,eventbox,{},true,&b) 
  #eventbox.realize
  eventbox.signal_connect('button_press_event') { |w, e| self.send(method_name,ret)  rescue error($!) }
  ret
end

#close_dialogObject



94
95
96
# File 'lib/ruiby_gtk/systray.rb', line 94

def close_dialog
  destroy
end

#color_choice(text = nil, options = {}, &cb) ⇒ Object

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()



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 342

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

#color_conversion(color) ⇒ Object



223
224
225
226
227
228
229
230
231
232
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 223

def color_conversion(color)
  case color 
    when ::Gdk::RGBA then color
    when String then color_conversion(::Gdk::Color.parse(color))
    when Array then color_conversion(color[1])
    when ::Gdk::Color then ::Gdk::RGBA.new(color.red/65535.0,color.green/65535.0,color.blue/65535.0,1)
    else
      raise "unknown color : #{color.inspect}"
  end
end

#combo(choices, default = nil, option = {}, &blk) ⇒ Object

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(%wbb cc,“bb”) { |text,index| alert(“#text at #index”) } w=combo(=> 20, “bb”=> 30, “cc”=> 40,0) { |text,index| alert(“#text at #index”) } w.get_selection() ==> [“aa”,20]



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 22

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

#css_name(name) ⇒ Object

give a name to last widget created. Useful for css style declaration



171
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 171

def css_name(name)  @current_widget ? @current_widget.set_name(name) : error("cssname #{name}: there are no current widget!") end

#current_layoutObject



37
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 37

def current_layout() @lcur.last end

#def_style(string_style = nil) ⇒ Object

define a set of css style, to be apply to every widget of main window if noparameter, load a file <caller>.rc



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/ruiby_gtk/dsl/commands.rb', line 114

def def_style(string_style=nil)
	unless string_style
	   fn=caller[0].gsub(/.rb$/,".rc")
	   raise "Style: no ressource (#{fn} not-exist)" if !File.exists?(fn)
	   string_style=File.read(fn)
	end
	begin
	  css=Gtk::CssProvider.new
	  css.load(data: string_style)
	  Ruiby.set_style_provider(css)
	  Ruiby.apply_provider(self)
	rescue Exception => e
	  error "Error loading style : #{e}\n#{string_style}"
	end
end

#delete(w) ⇒ Object

delete a widget or a timer thread protected



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/ruiby_gtk/dsl/commands.rb', line 89

def delete(w)
  if $__mainthread__ != Thread.current
    gui_invoke { delete(w) }
    return
  end
  if  Numeric === w && @hTimer[w]
    @hTimer[w]=false
  elsif  GLib::Timeout === w
    w.destroy
  else
    w.parent.remove(w) rescue error($!)
  end
end

#dialog(title = "") ⇒ Object

Dialog contents is build with bloc parameter. call is bloced until action on Ok/Nok/delete button return true if dialog quit is done by action on OK button

dialog(“title”) {

flow { button("dd") ... }

}



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 391

def dialog(title="") 
  dialog = Dialog.new(
    title: title,
    parent: self,
    buttons: [[Gtk::Stock::OK, :accept],
              [Gtk::Stock::CANCEL, :reject]]
  )
    
  @lcur << dialog.child
  hbox=stack { yield(dialog) }
  @lcur.pop
  
  dialog.set_window_position(:center)
  Ruiby.apply_provider(dialog.child)
  dialog.show_all 
  rep=dialog.run  #  blocked
  dialog.destroy
  rep==-3
end

#dialog_async(title, config = {}, &b) ⇒ Object

Dialog content is build with bloc parameter. Action on Ok/Nok/delete button make a call to :response bloc. dialog is destoy if return value of :response is true

dialog_async(“title”,:response=> bloc {|dia,e| }) {

flow { button("dd") ... }

}



333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 333

def dialog_async(title,config={},&b) 
  dialog = Dialog.new(
    title:   title,
    parent:  self,
    buttons: [[Gtk::Stock::OK, :accept],
              [Gtk::Stock::CANCEL, :reject]]
  )
          

  dialog.set_window_position(:center) if ! config[:position]
  
  @lcur << dialog.child
  hbox=stack { yield(dialog) }
  @lcur.pop
  Ruiby.apply_provider(dialog.child)
  if config[:response]
    dialog.signal_connect('response') do |w,response_id|
      rep=if response_id == Gtk::ResponseType::ACCEPT 
        config[:response].call(dialog,response_id) 
      else
        true
      end
      dialog.destroy if rep
    end
  else
    dialog.signal_connect('response') { |w,response_id| dialog.destroy }
  end
  dialog.show_all 
end

#entry(value, size = 10, option = {}, &blk) ⇒ Object

create a text entry for keyboard input if block defined, it while be trigger on eech of (character) change of the entry



180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 180

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

#exe(cmd, to = nil) ⇒ Object

execute a asynchonous system command, as done in a shell. output goes to log, pid of process is in $ruiby_script_pid on linux/unix host, exe() use PTY gem , on Windows it use popen3 to parameter is timeout of IO.select which wait for stdout output.



58
59
60
61
62
63
64
65
66
67
# File 'lib/ruiby_gtk/dsl/script.rb', line 58

def exe(cmd,to=nil)
  log cmd+" ..."
  if Dir.exist?("C:/")
    require 'open3'
    _exe_windows(cmd,to)
  else
    require 'pty'
    _exe_posix(cmd,to)
  end
end

#fentry(value, option = {}, &blk) ⇒ Object

create a integer text entry for keyboed input option must define :min :max :by for spin button



225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 225

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

#field(tlabel, lwidth, value, option = {}, &blk) ⇒ Object

show a label and a entry in a flow. entry widget is returned see fields()



240
241
242
243
244
245
246
247
248
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 240

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

#fields(alabel = [["nothing",""]], option = {}, &blk) ⇒ Object

show a stack of label/entry and buttons validation/annulation on button, bloc is invoked with the list of values of entrys



252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 252

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

#flow(config = {}, add1 = true, &b) ⇒ Object

container : horizontal box, take all space available, sloted in parent by default



11
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 11

def flow(config={},add1=true,&b)        _cbox(true,Box.new(:horizontal, 2),config,add1,&b) end

#flow_paned(size, fragment, &blk) ⇒ Object

create a container which can containe 2 widgets, separated by movable bar block invoked must create 2 widgets,horizonaly disposed



286
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 286

def flow_paned(size,fragment,&blk) _paned(true,size,fragment,&blk) end

#flowi(config = {}, add1 = true, &b) ⇒ Object

container : horizontal box, take only necessary space , sloted in parent



18
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 18

def flowi(config={},add1=true,&b)       _cbox(false,Box.new(:horizontal, 2),config,add1,&b) end

#force_update(canvas) ⇒ Object

update a canvas



376
# File 'lib/ruiby_gtk/dsl/canvas.rb', line 376

def force_update(canvas) canvas.queue_draw unless  canvas.destroyed?  end

#frame(t = "", config = {}, add1 = true, &b) ⇒ Object

a box with border and texte title, take all space



117
118
119
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 117

def frame(t="",config={},add1=true,&b)    
  w=_cbox(true,Frame.new(t),config,add1) { s=stack { b.call } ; s.set_border_width(5) } 
end

#framei(t = "", config = {}, add1 = true, &b) ⇒ Object

a box with border and texte title, take only necessary space



121
122
123
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 121

def framei(t="",config={},add1=true,&b)
  _cbox(false,Frame.new(t),config,add1) { s=stacki { b.call } ; s.set_border_width(5) }
end

#fslider(value = 0, option = {}, &b) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 103

def fslider(value=0,option={},&b)
  if DynVar === value
    return _dyn_fslider(value,option,&b)
  end
  w=Scale.new(:horizontal,option[:min],option[:max],option[:by])
  w.set_value(value ? value : 0)
  w.set_digits(option[:decimal]) if option[:decimal]
  w.signal_connect(:value_changed) { || b.call(w.value)  rescue error($!) } if block_given?
  w.define_singleton_method(:progress=) { |value| w.set_value(value) }
  attribs(w,option)   
end

#get_config(w) ⇒ Object

get a Hash aff all properties of a gtk widget



161
162
163
164
165
166
167
168
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 161

def get_config(w)
  return({"nil"=>""}) unless w && w.class.respond_to?("properties")
  w.class.properties().inject({"class"=>w.class.to_s}) { |h,meth| 
    data=(w.send(meth) rescue nil)
    h[meth]=data.inspect.gsub(/^#/,'')[0..32]  if data 
    h
  }
end

#get_current_containerObject



158
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 158

def get_current_container() @lcur.last end

#get_icon(name) ⇒ Object

obsolete



106
# File 'lib/ruiby_gtk/dsl/pixmap.rb', line 106

def get_icon(name) get_pixmap(name) end

#get_image(name) ⇒ Object

raster images access #############################



44
45
46
# File 'lib/ruiby_gtk/dsl/pixmap.rb', line 44

def get_image(name)
  Image.new(get_pixmap(name));
end

#get_image_from(name, size = :button) ⇒ Object

get a Image widget from a file or from a Gtk::Stock or famfamfam/crystal embeded in Ruiby. image can be a filename or a predefined icon in GTK::Stock or a famfamfam/crystal icon name (without .png) for file image, whe can specify a sub image (sqared) :

filename.png[NoCol , NoRow]xSize
filename.png[3,2]x32 : extract a icon of 32x32 pixel size from third column/second line
see samples/draw.rb


113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/ruiby_gtk/dsl/pixmap.rb', line 113

def get_image_from(name,size=:button)
  if name.index('.') 
    return Image.new(file: name) if File.exists?(name)
    return _sub_image(name) if name.index("[")
    alert("unknown icone #{name}")
  end
  iname=get_icon(name)
  if iname && Gdk::Pixbuf  === iname
    return Image.new(:pixbuf => iname) 
  elsif iname
    Image.new(:stock => iname,:size=> size)
  else
    nil
  end
end

#get_pixbuf(name) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/ruiby_gtk/dsl/pixmap.rb', line 131

def get_pixbuf(name)
  @cach_pix={} unless defined?(@cach_pix)
  if @cach_pix.size>100
    puts "purge cached pixbuf (>100)"
    @cach_pix={}
  end
  filename,px,py,bidon,dim=name.split(/\[|,|(\]x)/)
  if filename && px && py && bidon && dim && File.exist?(filename)
    dim=dim.to_i
    @cach_pix[filename]=Gdk::Pixbuf.new(:file =>filename) unless @cach_pix[filename]
    x0= dim*px.to_i
    y0= dim*py.to_i
    #p [x0,y0,"/",@cach_pix[filename].width,@cach_pix[filename].height]
    Gdk::Pixbuf.new(@cach_pix[filename],x0,y0,dim,dim)
  elsif File.exists?(name)
    unless @cach_pix[name]
      px=Gdk::Pixbuf.new(name) rescue error($!)
      @cach_pix[name]=px if px
    end
    @cach_pix[name]
  elsif ! name.index(".")
    get_stockicon_pixbuf(name)
  else
    raise("file #{name} not exist");
  end
end

#get_pixmap(name) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ruiby_gtk/dsl/pixmap.rb', line 48

def get_pixmap(name)
  if name=~ /^(famfamfam|crystal|farmfresh)/
    name=Dir.glob("#{Ruiby::MEDIA}/#{name.split(/\s+/).join("*")}*").first
  end    
  if name.index('.') 
    if File.exists?(name)
       @cach_pix||={}
       @cach_pix[name]=Gdk::Pixbuf.new(:file =>name) unless @cach_pix[name]
       return @cach_pix[name]
    elsif name.index("[")
      return get_pixbuf(name)
    end
    error("Unknown file #{name} for image raster")
    return(nil)
  end
  begin
    return get_stockicon_pixbuf(name)
  rescue Exception => e
    error("Unknown pixmap for icon '#{name}' :\n",e)
    return nil
  end
end

#get_stockicon_pixbuf(name) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/ruiby_gtk/dsl/pixmap.rb', line 71

def get_stockicon_pixbuf(name)
  begin
    icn="#{name.downcase.gsub('_','-')}"
    return Gtk::IconTheme.default().load_icon(icn,16,0)
  rescue Exception => ee
     if GTK2ICONNAME[icn]
      ( return Gtk::IconTheme.default().load_icon(GTK2ICONNAME[icn],16,0) ) rescue error($!)
     end
     puts ee.inspect
    return Gtk::IconTheme.default().load_icon("process-stop",48,0)
  end
end

#grid(names, w = 0, h = 0, options = {}) ⇒ Object

create a grid of data (as list, but multicolumn) use set_data() to put a 2 dimensions array of text same methods as list widget all columnes are String type



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/ruiby_gtk/dsl/list_grid.rb', line 104

def grid(names,w=0,h=0,options={})
  scrolled_win = Gtk::ScrolledWindow.new
  scrolled_win.set_policy(:automatic,:automatic)
  scrolled_win.set_width_request(w) if w>0
  scrolled_win.set_height_request(h)  if h>0

  model = Gtk::ListStore.new(*([String]*names.size))
  treeview = Gtk::TreeView.new(model)
  treeview.selection.set_mode(:single)
  names.each_with_index do  |name,i|
    treeview.append_column(
      Gtk::TreeViewColumn.new( name,Gtk::CellRendererText.new,{:text => i} )
    )
  end
  if block_given?
    treeview.signal_connect("row-activated") do |tview, path, column|
        sl=names.size.times.map {|i| tview.selection.selected[i].to_s.clone}
        yield(sl)
    end
  end

  def scrolled_win.grid() children[0].children[0] end
  def scrolled_win.model() grid().model end
  def scrolled_win.add_row(words)
    l=grid().model.append()
    words.each_with_index { |w,i| l[i] = w.to_s }
  end
  $ici=self
  def scrolled_win.get_data()
    raise("grid.get_data() out of main thread!")if $__mainthread__ != Thread.current
    @ruiby_data
  end
  def scrolled_win.set_data(data)
    grid().selection.unselect_all
    @ruiby_data=data
    raise("grid.set_data() out of main thread!")if $__mainthread__ != Thread.current
    grid().model.clear() ; data.each { |words| add_row(words) }
  end
  def scrolled_win.selection() a=grid().selection.selected ; a ? a[0] : nil ; end
  def scrolled_win.index() grid().selection.selected end
  scrolled_win.add_with_viewport(treeview)
  autoslot(nil)
  slot(scrolled_win)
end

#haccordionObject

create a horizontral accordion menu. must contain aitem() which must containe alabel() : accordion { aitem(txt) { alabel(lib) { code }; …} … }



244
245
246
247
248
249
250
251
252
253
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 244

def haccordion() 
  _accept?(:layout)
  @slot_accordion_active=nil #only one accordion active by window!
  w=flow { flowi {
    _set_accepter(@lcur.last,:aitem,:layout,:widget)
    yield
  }}
  separator
  w
end

#hide_appObject



89
90
91
92
# File 'lib/ruiby_gtk/systray.rb', line 89

def hide_app
  iconify
  hide
end

#hradio_buttons(ltext = ["empty!"], value = -1)) ⇒ Object

as vradio_buttons , but horizontaly disposed



117
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 117

def hradio_buttons(ltext=["empty!"],value=-1) _radio_buttons(:horizontal,ltext,value) end

#html_color(str) ⇒ Object

parse color from #RRggBB html format Ruiby_dsl.html_color



241
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 241

def html_color(str) color_conversion(str.kind_of?(Array) ? str.last : str) end

#htoolbar(options = {}) ⇒ Object

horizontal toolbar of icon button and/or separator if icon name contain a '/', second last is tooltip text Usage:

htoolbar { toolbat_button("text/tooltip" { } ; toolbar_separator ; ... }


254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 254

def htoolbar(options={})
  b=Toolbar.new
  b.set_toolbar_style(Gtk::ToolbarStyle::ICONS)
  _set_accepter(b,:toolb)
  @toolbarIndex=0
  @lcur << b
  yield
  @lcur.pop
  i=0    
  attribs(b,options)
  sloti(b)
end

#htoolbar_with_icon_text(conf = {}) ⇒ Object

horizontal toolbar of (icone+text)

htoolbar_with_icon_text do

button_icon_text "dialog_info","text info" do alert(1) end
button_icon_text "sep"

end

if icone name start with 'sep' : a vertical separator is drawn in place of touch see sketchi



296
297
298
299
300
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 296

def htoolbar_with_icon_text(conf={})
  flowi {
    yield
  }
end

#ientry(value, option = {}, &blk) ⇒ Object

create a integer text entry for keyboed input option must define :min :max :by for spin button



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 207

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

#image(file, options = {}) ⇒ Object

create a icon with a raster file option can specify a new size : :width and :height, or :size (square image)



41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 41

def image(file,options={})
  im=if File.exists?(file)
    pix=get_pixmap(file)
    pix=pix.scale(options[:width],options[:height],Gdk::Pixbuf::INTERP_BILINEAR) if options[:width] && options[:height]
    pix=pix.scale(options[:size],options[:size],Gdk::Pixbuf::INTERP_BILINEAR)  if options[:size] 
    Image.new(pixbuf: pix)
  else
    label("? "+file)
  end
  options.delete(:size)
  attribs(im,options)
end

#install_composant(window, componant) ⇒ Object



44
45
46
# File 'lib/ruiby_gtk/component.rb', line 44

def install_composant(window,componant)
   componant.install( window.instance_eval { @lcur } )
end

#islider(value = 0, option = {}, &b) ⇒ Object

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.



279
280
281
282
283
284
285
286
287
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 279

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

#label(text, options = {}) ⇒ Object

create label, with text (or image if txt start with a '#') spatial option : isize : icon size if image (menu,small_toolbar,large_toolbar,button,dnd,dialog)



22
23
24
25
26
27
28
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 22

def label(text,options={})
  if DynVar === text
    return _dyn_label(text,options)
  end
  l=_label(text,options)
  attribs(l,options)
end

#label_clickable(text, config = {}, &b) ⇒ Object

a label clikable label_button(“hello”) { alert(Time.now.to_s) }



90
91
92
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 90

def label_clickable(text,config={},&b)
   pclickable(proc { b.call()} ) { label(text) }  
end

#labeli(text, options = {}) ⇒ Object



29
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 29

def labeli(text,options={}) sloti(label(text,options)) end

#left(&blk) ⇒ Object

TODO : not tested!



97
98
99
100
101
102
103
104
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 97

def left(&blk) 
  autoslot()
  w=yield
  halign = Gtk::Alignment.new(0,0,0,0)
  halign.add(w)
  @lcur.last.pack_start(halign, :expand => false, :fill => false, :padding => 3)
  razslot()
end

#levelbar(start = 0, options) ⇒ Object



129
130
131
132
133
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 129

def levelbar(start=0,options)
 w=Gtk::LevelBar.new
 # TODO set value/progress/BynVar
 attribs(w,{})
end

#list(title, w = 0, h = 0, options = {}) ⇒ Object

create a verticale liste of data, with scrollbar if necessary define methods:

  • list() : get (gtk)list widget embeded

  • model() : get (gtk) model of the list widget

  • clear() clear content of the list

  • set_data(array) : clear and put new data in the list

  • selected() : get the selected items (or [])

  • index() : get the index of selected item (or [])

  • set_selection(index) : force current selection do no item in data

  • set_selctions(i0,i1) : force multiple consecutives selection from i1 to i2

if bloc is given, it is called on each selection, with array of index of item selectioned

Usage : list(“title”,100,200) { |li| alert(“Selections is : #Ruiby_dsl.ii.join(',')”) }.set_data(%wb c d)



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/ruiby_gtk/dsl/list_grid.rb', line 23

def list(title,w=0,h=0,options={})
  scrolled_win = Gtk::ScrolledWindow.new
  scrolled_win.set_policy(:automatic ,:automatic )
  scrolled_win.set_width_request(w) if w>0
  scrolled_win.set_height_request(h)  if h>0
  model = Gtk::ListStore.new(String)
  column = Gtk::TreeViewColumn.new(title.to_s,Gtk::CellRendererText.new, {:text => 0})
  treeview = Gtk::TreeView.new(model)
  if block_given?
    treeview.selection.signal_connect("changed") do |selection, path, column1|
      li=[];i=0;selection.each {|model1, path1, iter|  li << path1.to_s.to_i; i+=1 }
      ldata=[];model.each {|model, path, iter|  ldata << iter.get_value(0) }
      ld=li.map { |idx| ldata[idx] }
      yield(li,ld) rescue error($!)
    end
  end
  treeview.append_column(column)
  treeview.selection.set_mode(:multiple)
  scrolled_win.add_with_viewport(treeview)

  def scrolled_win.options(c) $__mainwindow__.apply_options(children[0].children[0],c) end
  def scrolled_win.list() children[0].children[0] end
  def scrolled_win.model() list().model end
  def scrolled_win.clear() list().model.clear end
  def scrolled_win.add_item(word)
    raise("list.add_item() out of main thread!") if $__mainthread__ != Thread.current
    list().model.append[0]=word
  end
  def scrolled_win.get_data()
    raise("list.get_data() out of main thread!") if $__mainthread__ != Thread.current
    puts
    l=[];list().model.each {|model, path, iter|
        l << iter.get_value(0)
    }
    l
  end
  def scrolled_win.set_data(words)
    raise("list.set_data() out of main thread!") if $__mainthread__ != Thread.current
    list().unselect_all
    list().model.clear
    words.each { |w| list().model.append[0]=w }
  end
  def scrolled_win.selection()
    li=[];i=0;list().selection.each {|model, path, iter|  li << path.to_s.to_i; i+=1 }
    li
  end
  def scrolled_win.index()
    li=[];i=0;list().selection.each {|model, path, iter|  li << path.to_s.to_i; i+=1 }
    li
  end
  def scrolled_win.set_selections(istart,istop)
    spath,epath=nil,nil
    i=0;model().each {|model, path, iter|
        if i==istart
          spath=path
        elsif i==istop
          epath=path
        end
        list().selection.unselect_path(path)
        i+=1
    }
    list().selection.select_range(spath,epath) if spath && epath
  end
  def scrolled_win.set_selection(index)
    model().each {|model, path, iter|  list().selection.unselect_path(path) }
    i=0;model().each {|model, path, iter|
        if i==index
          list().selection.select_path(path)
        end
        i+=1
    }
  end
  apply_options(treeview,options)
  autoslot(scrolled_win)
  scrolled_win
end

#log(*txt) ⇒ Object

put a line of message text in log dialog (create and show the log dialog if not exist)



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/ruiby_gtk/dsl/commands.rb', line 155

def log(*txt)
  if $__mainthread__ && $__mainthread__ != Thread.current
    gui_invoke { log(*txt) }
    return
  end
  loglabel=_create_log_window()
  buffer=loglabel.buffer
  tc=txt.map {|t| 
    t.kind_of?(Exception) ?  "#{t.to_s}\n  #{(t.backtrace||[]).join("\n  ")}\n" :  t 
  }.join(" ")
  txt_utf8= tc.encode("UTF-8", 'binary', invalid: :replace, undef: :replace, replace: '?')
  t="#{Time.now} | #{txt_utf8}\n"
  buffer.insert(buffer.end_iter,t)
  if ( loglabel.buffer.text.size>1000*1000)
    loglabel.buffer.text=loglabel.buffer.text[-7000..-1]
  end
  #----------  scroll to bottom, not perfect
  Ruiby.update
  vscroll=loglabel.parent.vadjustment
  vscroll.value = vscroll.upper+8
end

#log_as_widget(width = nil, height = nil, opt = {}) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/ruiby_gtk/dsl/commands.rb', line 221

def log_as_widget(width=nil,height=nil,opt={})
  logBuffer = TextBuffer.new
  loglabel=TextView.new(logBuffer)
  loglabel.override_font(  Pango::FontDescription.new( opt["font"] || "Courier new 10")) 
  sw=ScrolledWindow.new()
  sw.set_width_request(width) if width
  sw.set_height_request(height)  if height
  sw.set_policy(:automatic, :always)
  class << loglabel
    def add(s) 
      self.buffer.insert(self.buffer.end_iter,s)  
      self.scroll_mark_onscreen(self.buffer.create_mark("mend",self.buffer.start_iter.forward_to_end,false))
      self.parent.vadjustment.value=self.parent.vadjustment.upper - self.parent.vadjustment.page_size
    end
    def clear() self.buffer.text="" end
    def text=(s) self.buffer.text=s  end
  end
  sw.add_with_viewport(loglabel)
  apply_options(loglabel,opt)
  attribs(sw,{})
  @loglabel=loglabel
end

a vertial drop-down menu, only for menu_bar container



69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/ruiby_gtk/dsl/menus_popup.rb', line 69

def menu(text)
  _accept?(:menu)
  filem = MenuItem.new(text.to_s)
  @lcur.last.append(filem)
  mmenu = Menu.new()
  _set_accepter(mmenu,:menuitem)
  @lcur << mmenu
  yield
  @lcur.pop
  filem.submenu=mmenu
  show_all_children(mmenu)
  mmenu
end

Menu create a application menu. must contain menu() {} : menu_bar {menu(“F”) {menu_button(“a”) { } ; menu_separator; menu_checkbutton(“b”) { |w|} …}}



58
59
60
61
62
63
64
65
66
# File 'lib/ruiby_gtk/dsl/menus_popup.rb', line 58

def menu_bar()
  menuBar= MenuBar.new
  _set_accepter(menuBar,:menu)
  @lcur << menuBar
  yield
  @lcur.pop
  slot(menuBar)
  menuBar
end

create an text entry in a menu



84
85
86
87
88
89
# File 'lib/ruiby_gtk/dsl/menus_popup.rb', line 84

def menu_button(text="?",&blk)
  _accept?(:menuitem)
  item = MenuItem.new(text.to_s)
  @lcur.last.append(item)
  item.signal_connect("activate") { blk.call(text)  rescue error($!) }
end

create an checkbox entry in a menu



92
93
94
95
96
97
98
99
100
# File 'lib/ruiby_gtk/dsl/menus_popup.rb', line 92

def menu_checkbutton(text="?",state=false,&blk)
  _accept?(:menuitem)
  item = CheckMenuItem.new(label: text,use_underline: false)
  item.active=state
  @lcur.last.append(item)
  item.signal_connect("activate") {
    blk.call(item,text) rescue error($!.to_s)
  } 
end


101
102
103
104
# File 'lib/ruiby_gtk/dsl/menus_popup.rb', line 101

def menu_separator() 
  _accept?(:menuitem)
  @lcur.last.append( SeparatorMenuItem.new ) 
end

#next_rowObject



38
39
40
41
# File 'lib/ruiby_gtk/dsl/table.rb', line 38

def next_row()
  @ltable.last[:col]=0 # will be increment by cell..()
  @ltable.last[:row]+=1
end

#notebookObject

create a notebook widget. it must contain page() widgets notebook { page(“first”) { … } ; … }

nb.page=<no page>  => active no page


207
208
209
210
211
212
213
214
215
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 207

def notebook() 
  nb = Notebook.new()
  slot(nb)
  _set_accepter(nb,:tab)
  @lcur << nb
  yield
  @lcur.pop
  nb
end

#on_canvas_button_motion(&blk) ⇒ Object

define action on mouse button motion on current canvas definition



407
408
409
410
411
412
413
414
# File 'lib/ruiby_gtk/dsl/canvas.rb', line 407

def on_canvas_button_motion(&blk )
  _accept?(:handler)
  @currentCanvas.signal_connect('motion_notify_event')  { |w,e| 
    next unless w.get_memo()
    w.set_memo(blk.call(w,e,w.get_memo)) rescue error($!)
    force_update(w)
  }
end

#on_canvas_button_press(&blk) ⇒ Object

define action on button_press action must return an object whici will be transmit to motion/release handler



380
381
382
383
384
385
386
387
# File 'lib/ruiby_gtk/dsl/canvas.rb', line 380

def on_canvas_button_press(&blk)
  _accept?(:handler)
  @currentCanvas.signal_connect('button-press-event')   { |w,e| 
    ret=w.set_memo(blk.call(w,e))  rescue error($!)
    force_update(w) 
    ret
  }  
end

#on_canvas_button_release(&blk) ⇒ Object

define action on mouse button press on current canvas definition



397
398
399
400
401
402
403
404
405
# File 'lib/ruiby_gtk/dsl/canvas.rb', line 397

def on_canvas_button_release(&blk)
  _accept?(:handler)
  @currentCanvas.signal_connect('button_release_event') { |w,e| 
    ret=blk.call(w,e,w.get_memo) rescue error($!)
    w.set_memo(nil)
    force_update(w) 
    ret
  }  
end

#on_canvas_draw(&blk) ⇒ Object

define the drawing on current canvas definition



427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
# File 'lib/ruiby_gtk/dsl/canvas.rb', line 427

def on_canvas_draw(&blk)
  _accept?(:handler)
  @currentCanvas.signal_connect(  'draw' ) do |w,cr| 
      cr.set_line_join(Cairo::LINE_JOIN_ROUND)
      cr.set_line_cap(Cairo::LINE_CAP_ROUND)
      cr.set_line_width(2)
      cr.set_source_rgba(1,1,1,1)
      cr.paint
      begin
         w.instance_eval { @currentCanvasCtx=[w,cr] }
         blk.call(w,cr) 
         w.set_memo(false)
         #w.instance_eval { @currentCanvasCtx=nil }
      rescue Exception => e
       ( after(1) { error(e) } ) if w.get_memo()!=true
       w.draw_text(5,20,"Canvas ERROR...",1,"#EEE","#000")
       w.draw_text(5,30,e.to_s,1,"#EEE","#000")
       w.set_memo(true)
      end  
  end
end

#on_canvas_key_press(&blk) ⇒ Object

define action on keyboard press on current *window* definition



416
417
418
419
420
421
422
423
424
# File 'lib/ruiby_gtk/dsl/canvas.rb', line 416

def on_canvas_key_press(&blk)
  _accept?(:handler)
  p "signal key press"    
  @currentCanvas.signal_connect('key_press_event')  { |w,e| 
    p "signal key press  ok #{e}"    
    blk.call(w,e,Gdk::Keyval.to_name(e.keyval)) rescue error($!)
    force_update(w) 
  }
end

#on_canvas_resize(&blk) ⇒ Object



388
389
390
391
392
393
394
395
# File 'lib/ruiby_gtk/dsl/canvas.rb', line 388

def on_canvas_resize(&blk)
  _accept?(:handler)
  @currentCanvas.signal_connect('configure_event')   { |w,e| 
    ret=blk.call(w,e.width,e.height)  rescue error($!)
    force_update(w) 
    ret
  }  
end

#on_delete(&b) ⇒ Object

Arm a callback on window/dialog closed must return false for close the dialog dialog { stack ; on_delete { @isclosed=true ; false } }



381
382
383
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 381

def on_delete(&b)
    @lcur.last.toplevel.signal_connect("delete_event") { |w,e| b.call }
end

#page(title, icon = nil) ⇒ Object

a page widget. only for notebook container. button can be text or icone (if startin by '#', as label)



218
219
220
221
222
223
224
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 218

def page(title,icon=nil)
  _accept?(:tab)
  l=Image.new(pixbuf: get_pixmap(icon[1..-1])) if icon
  l=Image.new(pixbuf: get_pixmap(title[1..-1])) if title && title[0,1]=="#"
  l=Label.new(title) if  title.size>1 && title[0,1]!="#"
  @lcur.last.append_page( box { yield }, l )
end

#panel(title = "") ⇒ Object



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 410

def panel(title="") 
  dialog = Dialog.new(
    title: title,
    parent: self,
    buttons: []
  )
  ctx={}
  @lcur << dialog.child
  hbox=stack { yield(dialog,ctx) }
  @lcur.pop
  
  dialog.set_window_position(:center)
  Ruiby.apply_provider(dialog.child)
  dialog.show_all 
  rep=dialog.run  #  blocked
  dialog.destroy
  ctx
end

#panel_async(title) ⇒ Object

panel_async: a dialog without button



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 363

def panel_async(title) 
  dialog = Dialog.new(
    title:   title,
    parent:  self,
    buttons: []
  )
  dialog.set_window_position(:center) 
  
  @lcur << dialog.child
  stack { yield(dialog) }
  @lcur.pop
  Ruiby.apply_provider(dialog.child)

  dialog.show_all 
end

#panel_progress(text = "", &blk) ⇒ Object

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 " }
}


318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 318

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

#pclickable(aproc = nil, options = {}, &b) ⇒ Object

specific to gtk : some widget like label can't support click event, so they must be contained in a clickable parent (EventBox)

Exemple: pclickable(proc { alert true}) { label(“ click me! ”) }

bloc is evaluated in a stack container



157
158
159
160
161
162
163
164
165
166
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 157

def pclickable(aproc=nil,options={},&b) 
  _accept?(:layout)
  eventbox = Gtk::EventBox.new
  eventbox.events = Gdk::EventMask::BUTTON_PRESS_MASK
  ret=_cbox(true,eventbox,{},true,&b) 
  #eventbox.realize
  eventbox.signal_connect('button_press_event') { |w, e| aproc.call(w,e)  rescue error($!)  } if aproc
  apply_options(eventbox,options)
  ret
end

#pclickablie(aproc = nil, options = {}, &b) ⇒ Object

as pclickable, but container is a stacki pclickablei(proc { alert(“e”) }) { label(“click me!”) }



169
170
171
172
173
174
175
176
177
178
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 169

def pclickablie(aproc=nil,options={},&b) 
  _accept?(:layout)
  eventbox = Gtk::EventBox.new
  eventbox.events = Gdk::EventMask::BUTTON_PRESS_MASK
  ret=_cbox(false,eventbox,{},true,&b) 
  #eventbox.realize
  eventbox.signal_connect('button_press_event') { |w, e| aproc.call(w,e)  rescue error($!)  } if aproc
  apply_options(eventbox,options)
  ret
end

#plot(width, height, curves, config = {}) ⇒ Object

define a plot zone, with several curves :

pl=plot(400,200,{
    "curve1" => {
       data:[[0,1],[110,1],[20,1],[30,1],[10,1],[22,1],[55,1],[77,1]],
       color: '#FF0000', xminmax:[0,100], yminmax:[0,100], style: :linear,},...})
}

this methods are added :

  • pl.set_data(name,data) : replace current values par a new list of point [ [y,x],.…] for curve named 'name'

  • pl.get_data(name)

  • pl.add_curve(name,data:…) : add a curve

  • pl.delete_curve(name) : delete a curve

  • pl.add_data(name,pt) : add a point at the end of the curve

  • pl.scroll_data(name,value) : add a point at last and scroll if necessary (act as oscilloscope)

see samples/plot.rb



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/ruiby_gtk/dsl/plot.rb', line 20

def plot(width,height,curves,config={})
   plot=canvas(width,height) do
     on_canvas_draw { |w,ctx| w.expose(w,ctx) }
     on_canvas_button_press { |w,event| w.track(event)}
   end
   def plot.config(c) @config=c end
   plot.config(config.merge({w: width,h:height}))
   def plot.add_curve(name,config) 
      c=config.dup
      c[:type] = :curve
      c[:data] ||= [[0,0],[100,100]]
      c[:maxlendata] ||= c[:data].size
      c[:color] ||= "#003030"
      c[:rgba] = ::Gdk::Color.parse(c[:color])
      c[:bgrgba] = c[:bgcolor] ? ::Gdk::Color.parse(c[:bgcolor]) : nil
      c[:xminmax] ||= [c[:data].first[1],c[:data].last[1]]
      c[:yminmax] ||= [0,100]
      c[:style] ||= :linear
      c[:xa] = 1.0*width_request/(c[:xminmax][1]-c[:xminmax][0])
      c[:xb] = 0.0    -c[:xminmax][0]*c[:xa]
      c[:ya] = 1.0*height_request/(c[:yminmax][0]-c[:yminmax][1])
      c[:yb] = 0.0-c[:yminmax][1]*c[:ya]
      @curves[name]=c
   end
   def plot.add_bar(name,values)
      c={type: :bar,data:values, xmin: values.first.first , xmax: values.last.first}
      @curves[name]=c
   end
   def plot.delete_curve(name) 
      @curves.delete(name)
      redraw
   end
   def plot.expose(w,ctx) 
      return unless @curves
      w.draw_rectangle(0,0,@config[:w],@config[:h],0,nil,@config[:bg],0) if @config[:bg]
      if @config[:grid] 
        dx=dy=(@config[:grid]||"40").to_i
        color=@config[:grid_color] || "#AAA"
        0.step(width_request,dx) {|x| 
          w.draw_line([x,0,x,height_request],color,1) 
        }
        0.step(height_request,dy) {|y|
          w.draw_line([0,y,width_request,y],color,1)           
        }
      end
      yb0=3
      @curves.values.each do |c|
            next if c[:data].size<2
            case c[:type] 
            when :curve
                l=c[:data].each_with_object([]) { |(y,x),a|  
                  a << x*c[:xa]+c[:xb] ; a <<  y*c[:ya]+c[:yb] 
                }
                if c[:bgcolor]
                  l1= [0,c[:yb]] + l + [ c[:xminmax].last*c[:xa]+c[:xb] ,c[:yb]]
                  w.draw_polygon(l1,c[:bgcolor],c[:color],2)
                else
                  w.draw_line(l,c[:color],2)
                end
            when :bar
               w.draw_varbarr(0,@config[:h]-yb0,@config[:w],@config[:h]-yb0,c[:xmin],c[:xmax],c[:data],5)
               yb0+=5
            end
      end
      if @tx && @track_text
         w.draw_text_center(width_request/2,20,"Date: #{@track_title}",1.3,"#FFF","#000")
         w.draw_line([@tx,0,@tx,height_request],"#FFF",1)
         dx=(@tx<(width_request-70)) ? 10 : -10
         @track_text.each do |name,text,h,ht|
            w.draw_line([@tx,h,@tx+dx,ht],@curves[name][:color],1)
            if dx>0 
              w.draw_text(@tx+dx,ht,text,1,@curves[name][:color],"#000")
            else
              w.draw_text_left(@tx+dx,ht,text,1,@curves[name][:color],"#000")
            end
         end
      end
   end
   
   def plot.set_data(name,data) 
     @curves[name][:data]=data
     maxlen(name,@curves[name][:maxlendata])
     redraw
   end
   def plot.get_data(name) 
     @curves[name][:data]
   end
   def plot.add_data(name,pt) 
     @curves[name][:data] << pt
     maxlen(name,@curves[name][:maxlendata])
     redraw
   end
   def plot.scroll_data(name,value) 
      l=@curves[name][:data]
      pas=width_request/l.size
      l.each { |pt| pt[1]-=pas } 
      l << [ value , @curves[name][:xminmax].last ]
      maxlen(name,@curves[name][:maxlendata])
      redraw
   end
   def plot.maxlen(name,len)
     @curves[name][:data]=if @curves[name][:data].size>=len
      @curves[name][:data][-len..-1] 
     else
      @curves[name][:data]
     end
   end
   def plot.track(event)
    return unless @config[:tracker]
    x=nil
    lt=@curves.each_with_object([]) {|(name,d),a|
      next unless d[:type]==:curve
      x=(event.x-d[:xb])/d[:xa]
      y=psearch(d[:data],x)
      h=y*d[:ya]+d[:yb]
      a << [name,@config[:tracker][1].call(name,y) || "",h,(h>30) ? (h-10) : (h+10)]
    }
   50.times {
      t=false
      lt.each_with_index {|a,ia| h=a[3]
        ld=lt.each_with_index.select {|(n,t,h0,hh),ii| ii!=ia && (h-hh).abs<8}
        if ld.size>0
          ref=lt.each_with_object(0) {|(n,t,h0,hh),sum| sum+=(h-hh)}/ld.size
          moin=a[3]<20
          delta=(h-ref<0.1)? rand(h-2..h+2) : ((h-ref) > 0) ?  2 : -2 
          a[3]= [0,a[3]+delta,height_request].sort[1]
          t=true
          break 
        end
      }
      break unless t
    }
    @tx,@track_text,@track_title=event.x,lt,@config[:tracker][0].call(x)
   end
   def plot.psearch(lxy,x)
     imin,imax=0,(lxy.size-1)
     while imin<imax
        i= (imin+imax)/2
        vi=lxy[i][1]
        if vi<x
          imin=i+1
        elsif vi>x
          imax=i-1
        else
          return(lxy[i][0])
        end
     end
     return lxy[i][0]
   end

   def plot.init() @curves,@tx,@track_text={},nil,nil  end
   plot.init
   curves.each { |name,descr|  ; plot.add_curve(name,descr) }
   plot
end

Popup create a dynamic popup. popup block can be composed by pp_item and pp_separator Exemple : popup { pp_item(“text”) { } ; pp_seperator ; pp_item('Exit“) { exit!(0)} ; .…} popup can be rebuild by popup_clear_append(w)



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/ruiby_gtk/dsl/menus_popup.rb', line 12

def popup(w=nil)
  w ||= @lcur.last() 
  ppmenu = Gtk::Menu.new
  _set_accepter(ppmenu,:popupitem)
  @lcur << ppmenu 
  yield
  @lcur.pop
  ppmenu.show_all   
  w.add_events(Gdk::EventMask::BUTTON_PRESS_MASK)
  w.signal_connect("button_press_event") do |widget, event|
    ( after(300) {ppmenu.popup(nil, nil, event.button, event.time) { |menu, x, y, push_in| 
      [x,y] }} 
    ) if (event.button != 3)
  end
  ppmenu
end

clear a existant popup, rebuild it by bloc eval popup_clear_append(@pp) { pp_item(..) ; pp_separator() .…}



46
47
48
49
50
51
52
53
# File 'lib/ruiby_gtk/dsl/menus_popup.rb', line 46

def popup_clear_append(pp)
  pp.children.each { |c| delete c}
  _set_accepter(pp,:popupitem)
  @lcur << pp
  yield rescue error($!)
  @lcur.pop
  pp.show_all   
end

#pp_item(text, &blk) ⇒ Object

a button in a popup



30
31
32
33
34
35
# File 'lib/ruiby_gtk/dsl/menus_popup.rb', line 30

def pp_item(text,&blk)
  _accept?(:popupitem)
  item = Gtk::MenuItem.new(text)
  item.signal_connect('activate') { |w| blk.call() rescue error($!) }
  @lcur.last.append(item)
end

#pp_separatorObject

a bar separator in a popup



38
39
40
41
42
# File 'lib/ruiby_gtk/dsl/menus_popup.rb', line 38

def pp_separator()
  _accept?(:popupitem)
  item = Gtk::SeparatorMenuItem.new()
  @lcur.last.append(item)
end

#progress(value = 0, text = nil, option = {}) ⇒ Object

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)


295
296
297
298
299
300
301
302
303
304
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 295

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

#progress_bar(start = 0, options) ⇒ Object

Show the evolution if a numeric value. Evolution is a number between 0 and 1.0 w.progress=n force current evolution



123
124
125
126
127
128
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 123

def progress_bar(start=0,options)
 w=Gtk::ProgressBar.new
 w.define_singleton_method(:progress=) { |fract| w.fraction=fract }
 w.fraction=start
 attribs(w,{})
end

#properties(title, hash, options = {:edit=>false, :scroll=>[0,0]}) ⇒ Object

create a property shower/editor : vertical liste of label/entry representing the ruby Hash content Edition: Option: use :edit => true for show value in text entry, and a validate button, on button action, yield of bloc parameter is done with modified Hash as argument widget define set_data()methods for changing current value



355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 355

def properties(title,hash,options={:edit=>false, :scroll=>[0,0]})
  if ! defined?(@prop_index)
    @prop_index=0
    @prop_hash={}
  else
    @prop_index+=1
  end
  prop_current=(@prop_hash[@prop_index]={})
  value={}
  widget=stacki {
  framei(title.to_s) {
     stack {
      if options[:scroll] &&  options[:scroll][1]>0
       vbox_scrolled(options[:scroll][0],options[:scroll][1]) {
         table(2,hash.size) {
          hash.each { |k,v| row {
            _make_prop_line(prop_current,options,k,v)
          }}
          }
        }
      else
       table(2,hash.size) {
        hash.each { |k,v| row {
            _make_prop_line(prop_current,options,k,v)
        }}
        }
      end
        if options[:edit]
          sloti(button("Validation") { 
            nhash=widget.get_data()
            if block_given? 
              yield(nhash)
            else
              hash.clear
              nhash.each { |k,v| hash[k]=v }
            end
          }) 
        end
      }
   }
  } 
  widget.instance_variable_set(:@prop_current,prop_current)   
  widget.instance_variable_set(:@hash_initial,hash)   
  def widget.set_data(newh)
    newh.each { |k,v| @prop_current[k].text=v.to_s }
  end
  def widget.get_data()
    @prop_current.inject({}) {|nhash,(k,w)| 
    v_old=@hash_initial[k]
    v_new=w.text
    vbin=case v_old 
      when String then v_new
      when Fixnum then v_new.to_i
      when Float  then v_new.to_f
      when /^(\[.*\])|(\{.*\})$/ then eval( v_new ) rescue error($!)
      else v_new.to_s
    end
    nhash[k]=vbin
    nhash
    }
  end
  widget
end

#propertys(title, hash, options = {:edit=>false, :scroll=>[0,0]}, &b) ⇒ Object

deprecated: see properties



337
338
339
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 337

def propertys(title,hash,options={:edit=>false, :scroll=>[0,0]},&b)
  properties(title,hash,options,&b)
end

#razslotObject

forget precedent widget oconstructed



158
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 158

def razslot() @current_widget=nil; end

#regular(on = true) ⇒ Object

set homogeneous contrainte on current container : all chidren whill have same size

  • stack : children will have same height

  • flow : children will have same width



77
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 77

def regular(on=true) @lcur.last.homogeneous=on end

#right(&blk) ⇒ Object

TODO : not tested!



106
107
108
109
110
111
112
113
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 106

def right(&blk) 
  autoslot()
  w=yield
  halign = Gtk::Alignment.new(1,0,0,0)
  halign.add(w)
  @lcur.last.pack_start(halign, :expand => false, :fill => false, :padding => 3)
  razslot()
end

#rowObject

create a row. must be defined in a table closure Closure argment should only contain cell(s) call. many cell type are disponibles : cell cell_bottom cell_hspan cell_hspan_left cell_hspan_right cell_left cell_pass cell_right cell_span cell_top cell_vspan cell_vspan_bottom cell_vspan_top row do

cell( label("ee")) ; cell_hspan(3, button("rr") ) }

end



31
32
33
34
35
36
37
# File 'lib/ruiby_gtk/dsl/table.rb', line 31

def row()
  autoslot()
  _accept?(:row)
  @ltable.last[:col]=0 # will be increment by cell..()
  yield
  @ltable.last[:row]+=1
end

#script(caption = "Parameters", nb_column = 2, hctx = nil) ⇒ Object

script ################## define a hmi corresponding to a script command. see samples/script.rb the layout created contains three zones:

  • parameters : a set of entry, created with a DynObject which descriptor is hctx

  • button zone : a table of widgets. widget are created with bloc traitment,

  • a log zone : scolling area on text, appended with log() commande

  • bottom fixed buttons : clear log and exit.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ruiby_gtk/dsl/script.rb', line 13

def script(caption="Parameters",nb_column=2,hctx=nil) 
  $script_pid=nil
  @ctx=make_StockDynObject("ctx",hctx) if hctx
  stack do
    stacki do
      button_expand(caption+"...") do
        table(0,0) do
          row {
            @ctx.keys.each {|key|
              value=@ctx.send(key)
              cell_right(label "#{key.gsub('_',' ')} : ")
              cell_hspan(2,entry(value,{font: 'Courier 10'}))
              next_row
            }
          }
        end
      end if hctx
      @st=stack do yield end
      table(0,0) do
        row {
          @st.children.each_slice(nb_column) { |lb|
            lb.each {|w| w.parent.remove(w) ; cell(w) }
            next_row
          }
        }
      end
      delete(@st)
    end
    @log=text_area(100,100,{font: 'Courier 8', bg: "#004444", fg: "#FFF"})
    flowi do
      button("Clear log") { @log.text=""}
      buttoni("abort") { after(0) {
        Process.kill(9,$ruiby_script_pid) if $ruiby_script_pid; $ruiby_script_pid=nil
        } 
      } 
      buttoni("Exit") { after(0) {exit()} } 
    end 
  end
  self.class.instance_eval { define_method("log") do |*args|  @log.append(args.join(" ")+"\n") end }
end

#scrolled(width, height, &b) ⇒ Object

create a Scrolled widget with a autobuild stack in it stack can be populated respond to : scroll_to_top; scroll_to_bottom,



182
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 182

def scrolled(width,height,&b)  vbox_scrolled(width,height,&b) end

#sentence(config = {}, add = true, &b) ⇒ Object

container manage children has sentence flow widgets



23
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 23

def sentence(config={},add=true,&b) _cbox(true,FlowBox.new(),config,add,&b) end

#sentenci(config = {}, add = true, &b) ⇒ Object



24
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 24

def sentenci(config={},add=true,&b) _cbox(false,FlowBox.new(),config,add,&b) end

#separator(width = 1.0) ⇒ Object

create a bar (vertical or horizontal according to stack/flow current container)



7
8
9
10
11
12
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 7

def separator(width=1.0)  
  autoslot()
  sloti( Separator.new(
    @lcur.last.orientation==Gtk::Orientation::VERTICAL ? 
      Gtk::Orientation::HORIZONTAL : Gtk::Orientation::VERTICAL))  
end

#show_all_children(c) ⇒ Object



51
52
53
54
# File 'lib/ruiby_gtk/dsl/commands.rb', line 51

def show_all_children(c)
  return unless c
  c.each { |f|   show_all_children(f) if  f.respond_to?(:children) ; f.show() } ; c.show
end

#show_appObject



85
86
87
88
# File 'lib/ruiby_gtk/systray.rb', line 85

def show_app()
  deiconify 
  show 
end

#show_methods(obj = nil, filter = nil) ⇒ Object

show methods of a object/class in log window



328
329
330
331
332
333
334
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 328

def show_methods(obj=nil,filter=nil)
  obj=self unless obj
  title="\n============ #{Class===obj.class ? obj : obj.class} ===========\n"
  data=(obj.methods-Object.methods).grep(filter || /.*/).sort.each_slice(3).map { |a,b,c| "%-30s| %-30s| %-30s" % [a,b,c]}.join("\n")
  footer="\n==================================================\n"
  log( title+data+footer)
end

#show_sourceObject

a button for show source code of application. usefule for demos app



84
85
86
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 84

def show_source
  label_clickable("Show source...") { Editor.new(self,$0,500) }
end

#slider(start = 0.0, min = 0.0, max = 1.0, options = {}) ⇒ Object

Create a horizontal bar with a stick which can be moved. block (if defined) is invoked on each value changed w.proess=n can force current position at n



99
100
101
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 99

def slider(start=0.0,min=0.0,max=1.0,options={})
  fslider(start,{min: min, max: max, by: options[:by]||1}.merge(option))
end

#slot(w) ⇒ Object

pack widget in parameter, share space with prother widget this is the default: all widget will be sloted if they are not slotied this is done by attribs(w) which is call after construction of almost all widget



146
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 146

def slot(w)  @current_widget=nil; _pack(@lcur.last,w,true) ; w end

#slot_append_after(w, wref) ⇒ Object

append the widget w after anotherone wref) thread protected



73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/ruiby_gtk/dsl/commands.rb', line 73

def slot_append_after(w,wref)
  if $__mainthread__ != Thread.current
    gui_invoke { slot_append_after(w,wref) }
    return
  end
  parent=_check_append("slot_append_after",w,wref)
  parent.add(w)
  parent.children.each_with_index { |child,i| 
    next if child!=wref
    parent.reorder_child(w,i+1)
    break
  }
  w
end

#slot_append_before(w, wref) ⇒ Object

append the widget w before another one wref thread protected



57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/ruiby_gtk/dsl/commands.rb', line 57

def slot_append_before(w,wref)
  if $__mainthread__ != Thread.current
    gui_invoke { slot_append_before(w,wref) }
    return
  end
  parent=_check_append("slot_append_before",w,wref)
  parent.add(w)
  parent.children.each_with_index { |child,i| 
    next if child!=wref
    parent.reorder_child(w,i)
    break
  }
  w
end

#sloti(w) ⇒ Object

pack widget in parameter, take only necessary space



149
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 149

def sloti(w) @current_widget=nil; @lcur.last.pack_start(w, :expand => false, :fill => false, :padding => 0) ; w end

#snapshot(filename = nil) ⇒ Object

make a snapshot raster file of current window can be called by user. Is called by mainloop if string 'take-a-snapshot' is present in ARGV only for Windows !!!



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/ruiby_gtk/dsl/commands.rb', line 135

def snapshot(filename=nil)
   return unless  RUBY_PLATFORM =~ /in.*32/
   require 'win32/screenshot'
   require 'win32ole'
   
   filename=Time.now.strftime("%D-%H%m%s.png").gsub('/','-') unless filename
   
   # window must have a title...
   if ! self.title || self.title.size<3
      self.title=Time.now.to_f.to_s.gsub('.','')
   end
  File.delete(filename) if File.exists?(filename)
  puts "generated  for title '#{self.title}' ==> #{filename} ..."
  Win32::Screenshot::Take.of(:window,:title => /#{self.title}/, :context => :window).write(filename)
  puts "snapshot done, size= #{File.size(filename)/1024} KB, name=#{filename}"
end

#source_editor(args = {}, &blk) ⇒ Object

a source_editor widget : text as showed in fixed font, colorized (default: ruby syntaxe) from: green shoes plugin options= :width :height :on_change :lang :font @edit=source_editor().editor @edit.buffer.text=File.read(@filename)



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/ruiby_gtk/dsl/editors.rb', line 13

def source_editor(args={},&blk) 
  #return(nil) # loading gtksourceview3 scratch application...
  begin
    require 'gtksourceview3'
  rescue Exception => e
    log('gtksourceview3 not installed!, please use text_area')
    puts '******** gtksourceview3 not installed!, please use text_area ************' 
    return
  end
  _accept?(:widget)    
  args[:width]  = 400 unless args[:width]
  args[:height] = 300 unless args[:height]
  change_proc = proc { }
  sv = GtkSource::View.new
  sv.show_line_numbers = true
  sv.insert_spaces_instead_of_tabs = false
  sv.smart_home_end = :always
  sv.tab_width = 4
  sv.buffer.text = (args[:text]||"").to_s
  sv.buffer.language = GtkSource::LanguageManager.new.get_language(args[:lang]||'ruby')
  sv.buffer.highlight_syntax = true
  sv.override_font(  Pango::FontDescription.new(args[:font] || "Courier new 10")) 
  cb = ScrolledWindow.new
  cb.define_singleton_method(:editor) { sv }
  cb.define_singleton_method(:text=) { |t| sv.buffer.text=t }
  cb.define_singleton_method(:text) {  sv.buffer.text }
  
  if block_given?
    sv.signal_connect('key_press_event') { |w,evt|
        blk.call(w,w.buffer.text) rescue error($!)
        false
    }
  end
  
  cb.set_size_request(args[:width], args[:height])
  cb.set_policy(:automatic, :automatic)
  cb.set_shadow_type(:in)
  cb.add(sv)
  cb.show_all
  attribs(cb,{})  
end

#space(n = 1) ⇒ Object

create a one-character size space, (or n character x n line space)



54
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 54

def space(n=1) label(([" "*n]*n).join("\n"))  end

#spacei(n = 1) ⇒ Object



55
# File 'lib/ruiby_gtk/dsl/label_button_image.rb', line 55

def spacei(n=1) labeli(([" "*n]*n).join("\n"))  end

#spacing(npixels = 0) ⇒ Object

set space between each chidren of current box



80
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 80

def spacing(npixels=0) @lcur.last.spacing=npixels end

#stack(config = {}, add1 = true, &b) ⇒ Object

container : vertical box, take all space available, sloted in parent by default



9
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 9

def stack(config={},add1=true,&b)       _cbox(true,Box.new(:vertical, 2),config,add1,&b) end

#stack_paned(size, fragment, &blk) ⇒ Object

create a container which can containe 2 widgets, separated by movable bar block invoked must create 2 widgets, vertivaly disposed



282
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 282

def stack_paned(size,fragment,&blk) _paned(false,size,fragment,&blk) end

#stacki(config = {}, add1 = true, &b) ⇒ Object

container : vertical box, take only necessary space , sloted in parent



16
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 16

def stacki(config={},add1=true,&b)      _cbox(false,Box.new(:vertical, 2),config,add1,&b) end

#syst_add_button(label, &prc) ⇒ Object



78
# File 'lib/ruiby_gtk/systray.rb', line 78

def syst_add_button(label,&prc)  @systray_config[label]=prc     ; end

#syst_add_check(label, &prc) ⇒ Object



80
# File 'lib/ruiby_gtk/systray.rb', line 80

def syst_add_check(label,&prc)   @systray_config["+"+label]=prc ; end

#syst_add_sepratatorObject



79
# File 'lib/ruiby_gtk/systray.rb', line 79

def syst_add_sepratator()        @systray_config["--#{@systray_config.size}"]= proc {} ; end

#syst_icon(file) ⇒ Object



77
# File 'lib/ruiby_gtk/systray.rb', line 77

def syst_icon(file)            @systray_config[:icon]=file     ; end

#syst_quit_button(yes) ⇒ Object



81
# File 'lib/ruiby_gtk/systray.rb', line 81

def syst_quit_button(yes)       @systray_config[:quit]=yes       ; end

#systray(x = nil, y = nil, systray_config = {}) ⇒ Object



71
72
73
74
75
76
# File 'lib/ruiby_gtk/systray.rb', line 71

def systray(x=nil,y=nil,systray_config={})
  @title=File.basename($0).gsub(/\.rb/,'') unless defined?(@title)
  @systray_config=systray_config
  yield
  @systray=::Gtk::SysTray.new(self,@title,@systray_config,x,y)
end

#systray_setup(config) ⇒ Object



82
83
84
# File 'lib/ruiby_gtk/systray.rb', line 82

def systray_setup(config)
  @systray=::Gtk::SysTray.new(self,@title,config)
end

#table(nb_col = 0, nb_row = 0, config = {}) ⇒ Object

table create a container for table-disposed widgets. this is not a grid! table(r,c) { row { cell(w) ; .. } ; … } or this form : table { cell(w) ; cell(w2) ; next_row ; cell(w3), cell(w4) }



11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/ruiby_gtk/dsl/table.rb', line 11

def table(nb_col=0,nb_row=0,config={})
  autoslot
  table = Gtk::Table.new(nb_row,nb_col,false)
  table.set_column_spacings(config[:set_column_spacings]) if config[:set_column_spacings]
  _set_accepter(table,:row,:cell,:widget)
  @lcur << table
  @ltable << { :row => 0, :col => 0}
  yield
  @ltable.pop
  @lcur.pop
  attribs(table,config)
end

#text_area(w = 200, h = 100, args = {}) ⇒ Object

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



376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 376

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

#text_area_dyn(dynvar, w = 200, h = 100, args = {}) ⇒ Object

multiline entry on dynvar



360
361
362
363
364
365
366
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 360

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

#toggle_button(text1, text2 = nil, value = false, option = {}, &blk) ⇒ Object

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



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/ruiby_gtk/dsl/form_fields.rb', line 58

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

#toolbar_button(name, tooltip = nil, &blk) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 267

def toolbar_button(name,tooltip=nil,&blk)
    _accept?(:toolb)
    iname=get_icon(name)
    
    w=Gtk::ToolButton.new(icon_widget: get_image(name) )
    w.signal_connect("clicked") { blk.call rescue error($!) } if blk
    w.set_tooltip_text(tooltip) if tooltip
    
    @lcur.last.insert(w,@toolbarIndex)
    @toolbarIndex+=1
end

#toolbar_separatorObject



278
279
280
281
282
283
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 278

def toolbar_separator()
    _accept?(:toolb)
    w=Gtk::SeparatorToolItem.new        
    @lcur.last.insert(w,@toolbarIndex)
    @toolbarIndex+=1
end

#tooltip(value = "?") ⇒ Object

give a tooltip to last widget created.



174
175
176
177
178
179
180
181
# File 'lib/ruiby_gtk/ruiby_dsl3.rb', line 174

def tooltip(value="?") 
  if @current_widget && @current_widget.respond_to?(:set_tooltip_markup)
    @current_widget.set_tooltip_markup(value) 
    @current_widget.has_tooltip=true
  else
    error("tooltip : '#{value[0..30]}' : there are no current widget or it cannot contain ToolTip !") 
  end
end

#trace(*txt) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/ruiby_gtk/dsl/commands.rb', line 176

def trace(*txt)
  if $__mainthread__ && $__mainthread__ != Thread.current
    gui_invoke { log(*txt) }
    return
  end
  loglabel=_create_log_window()
  buffer=loglabel.buffer
  tc=txt.map {|t| 
    t.kind_of?(Exception) ?  "#{t.to_s}\n  #{(t.backtrace||[]).join("\n  ")}\n" :  t 
  }.join(" ")
  txt_utf8= tc.encode("UTF-8", 'binary', invalid: :replace, undef: :replace, replace: '?')
  t=" #{txt_utf8}\n"
  buffer.insert(buffer.end_iter,t)
  if ( loglabel.buffer.text.size>1000*1000)
    loglabel.buffer.text=loglabel.buffer.text[-7000..-1]
  end
  #----------  scroll to bottom, not perfect
  Ruiby.update
  vscroll=loglabel.parent.vadjustment
  vscroll.value = vscroll.upper+8
end

#tree_grid(names, w = 0, h = 0, options = {}) ⇒ Object

create a tree view of data (as grid, but first column is a tree) use set_data() to put a Hash of data same methods as grid widget a columns Class are distinges by column name : <li> raster image if name start with a '#' <li> checkbutton if name start with a '?' <li> Integer if name start with a '0' <li> String else



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/ruiby_gtk/dsl/list_grid.rb', line 157

def tree_grid(names,w=0,h=0,options={})
  scrolled_win = Gtk::ScrolledWindow.new
  scrolled_win.set_policy(:automatic,:automatic)
  scrolled_win.set_width_request(w) if w>0
  scrolled_win.set_height_request(h)  if h>0
  scrolled_win.shadow_type = :etched_in

  types=names.map do |name|
   case name[0,1]
    when "#" then Gdk::Pixbuf
    when "?" then TrueClass
    when "0".."9" then Integer
    else String
   end
  end
  model = Gtk::TreeStore.new(*types)

  treeview = Gtk::TreeView.new(model)
  treeview.selection.set_mode(:single)
  names.each_with_index do  |name,i|
    renderer,symb= *(
      if    types[i]==TrueClass then   [Gtk::CellRendererToggle.new().tap { |r| r.signal_connect('toggled') { } },:window]
      elsif types[i]==Gdk::Pixbuf then [Gtk::CellRendererPixbuf.new,:active]
      elsif types[i]==Numeric then   [Gtk::CellRendererText.new,:text]
      else               [Gtk::CellRendererText.new,:text]
      end
    )
    treeview.append_column(
      Gtk::TreeViewColumn.new( name.gsub(/^[#?0-9]/,""),renderer,{symb => i} )
    )
  end

  #------------- Build singleton

  def scrolled_win.init(types) @types=types end
  scrolled_win.init(types)
  def scrolled_win.tree() children[0].children[0] end
  def scrolled_win.model() tree().model end
  $ici=self
  def scrolled_win.get_data()
    raise("tree.get_data() out of main thread!")if $__mainthread__ != Thread.current
    @ruiby_data
  end
  def scrolled_win.set_data(hdata,parent=nil,first=true)
    raise("tree.set_data() out of main thread!")if $__mainthread__ != Thread.current
    if parent==nil && first
      @ruiby_data=hdata
      model.clear()
    end
    hdata.each do |k,v|
      case v
        when Array
          set_row([k.to_s]+v,parent)
        when Hash
          p=model.append(parent)
          p[0] =k.to_s
          set_data(v,p,false)
      end
    end
  end
  def scrolled_win.set_row(data,parent=nil)
    puts "treeview: raw data size nok : #{data.size}/#{data.inspect}" if data.size!=@types.size
    i=0
    c=self.model.append(parent)
    data.zip(@types) do |item,clazz|
      c[i]=if clazz==TrueClass then (item ? true : false)
        elsif clazz==Gdk::Pixbuf then $ici.get_pixbuf(item.to_s).tap {|a| p [item,clazz,a]}
        elsif clazz==Integer then item.to_i
        else item.to_s
      end
      i+=1
    end
  end
  def scrolled_win.selection() a=tree().selection.selected ; a ? a[0] : nil ; end
  def scrolled_win.index() tree().selection.selected end

  scrolled_win.add_with_viewport(treeview)
  apply_options(treeview,options)
  autoslot(nil)
  slot(scrolled_win)
end

#updateObject



114
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 114

def update() Ruiby.update() end

#var_box(sens, config = {}, add1 = true, &b) ⇒ Object

container : vertical or horizontal box (stack/flow, choice by first argument), sloted in parent by default



14
# File 'lib/ruiby_gtk/dsl/layouts.rb', line 14

def var_box(sens,config={},add1=true,&b) _cbox(true,Box.new(sens, 2),config,add1,&