Module: SimpleLayout::Base

Defined in:
lib/simple_layout.rb

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(sym, *args, &block) ⇒ Object (private)



589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
# File 'lib/simple_layout.rb', line 589

def method_missing(sym, *args, &block)
  if sym.to_s =~ /^(.+)_in(_.+)$/
    maps = self.class.layout_class_maps
    inner, outter = $1, $2
    if maps[inner] && maps[outter]
      if args.last.is_a?(Hash)
        options = {}
        options = args.pop if args.last.is_a?(Hash)
        # default args pass to inner component, execpt:
        #  :layout pass to outter :layout
        #  :inner_layout pass to inner :layout
        #  :outter_args pass to outter args
        outter_args, outter_layout_opt, options[:layout] =
          options.delete(:outter_args), options.delete(:layout), options.delete(:inner_layout)
        outter_args = (outter_args ? [outter_args] : []) unless outter_args.is_a?(Array)
        outter_args << {} unless outter_args.last.is_a?(Hash)
        outter_args.last[:layout] ||= outter_layout_opt
        args.push options # push back inner options
      end
      inner_proc = Proc.new do
        create_component(maps[inner], args, block)
      end
      return create_component(maps[outter], outter_args || [], inner_proc)
    end
  end
  simple_layout_method_missing_alias(sym, *args, &block)
end

Class Method Details

.included(base) ⇒ Object



113
114
115
116
117
118
119
120
# File 'lib/simple_layout.rb', line 113

def Base.included(base)
  base.extend(ExtClassMethod)
  base.layout_class_maps.each do |k, v|
    define_method(k) do |*args, &block|
      create_component(v, args, block)
    end
  end
end

Instance Method Details

#add_component(w, container, layout_opt) ⇒ Object

add a widget to container (and/or become a new container as well). do not call this function directly unless knowing what you are doing



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
# File 'lib/simple_layout.rb', line 148

def add_component(w, container, layout_opt)
  if @pass_on_stack.last.nil? || @pass_on_stack.last[0] == false
    if container.is_a?(Gtk::Box)
      layout_opt ||= [expand: false, fill: false, padding: 0]
      pack_method = 'pack_start'
      railse LayoutError, "layout_opt should be an Array" unless layout_opt.is_a?(Array)
      if layout_opt.first.is_a?(Symbol) && layout_opt[0] == :end
        layout_opt.shift  # remove the first ':end'
        pack_method = 'pack_end'
      end
      if layout_opt.size == 1 && layout_opt.last.is_a?(Hash)
        # if there is only one Hash in layout_opt, it's the keyword arguments for pack_start or pack_end
        container.send(pack_method, w, **layout_opt.last)
      else
        # else it's the position arguments, old style, we need to conver to keyword arguments
        opt = {expand: layout_opt[0], fill: layout_opt[1], padding: layout_opt[2]}
        container.send(pack_method, w, **opt)
      end
    elsif container.is_a?(Gtk::Fixed) || container.is_a?(Gtk::Layout)
      layout_opt ||= [0, 0]
      container.put w, *layout_opt
    elsif container.is_a?(Gtk::MenuShell) || container.is_a?(Gtk::MenuBar) || container.is_a?(Gtk::Menu)
      container.append w
    elsif container.is_a?(Gtk::MenuItem)
      container.submenu = w
    elsif container.is_a?(Gtk::Toolbar)
      container.insert(container.n_items, w)
    elsif container.is_a?(Gtk::MenuToolButton)
      container.menu = w
    elsif container.is_a?(Gtk::ScrolledWindow)
      container.add_with_viewport(w)
      if layout_opt && layout_opt.size > 0
        if layout_opt.size == 1 && layout_opt.first.is_a?(Hash)
          container.set_policy(**layout_opt.first)
        else
          container.set_policy(*layout_opt)
        end
      end
    elsif container.is_a?(Gtk::Table)
      # should use #grid or #grid_flx to add a child to Table
    elsif container.is_a?(Gtk::Notebook)
      # should use #page to add a child to Notebook
    elsif container.is_a?(Gtk::Paned)
      # should use #area_first or #area_second to add child to Paned
    elsif container.is_a?(Gtk::Container) || container.respond_to?(:add)
      # lastly, if it's a general container or respond to 'add', use #add to add child
      args = [w, *layout_opt].flatten
      container.add(*args)
    end
  else
    fun_name, args = *(@pass_on_stack.last[1])
    container.send(fun_name, w, *args)
  end
end

#area_first(resize = true, shrink = true, &block) ⇒ Object

for HPaned and VPaned container



245
246
247
# File 'lib/simple_layout.rb', line 245

def area_first(resize = true, shrink = true, &block)
  container_pass_on(Gtk::Paned, 'pack1', resize, shrink, block)
end

#area_second(resize = true, shrink = true, &block) ⇒ Object



248
249
250
# File 'lib/simple_layout.rb', line 248

def area_second(resize = true, shrink = true, &block)
  container_pass_on(Gtk::Paned, 'pack2', resize, shrink, block)
end

#component(name) ⇒ Object

get component with given name



215
216
217
# File 'lib/simple_layout.rb', line 215

def component(name)
  @components[name]
end

#component_children(name) ⇒ Object

return children array of a component or group



220
221
222
223
# File 'lib/simple_layout.rb', line 220

def component_children(name)
  @component_children ||= {}
  @component_children[name]
end

#expose_componentsObject

expose the components as instance variables



136
137
138
139
140
141
142
143
144
# File 'lib/simple_layout.rb', line 136

def expose_components()
  @components.each_key do |k|
    unless self.respond_to?(k.to_s, true)
      self.instance_eval("def #{k.to_s}; component(:#{k.to_s}) end")
    else
      raise LayoutError, "#{k} is conflit with method, please redifine component id"
    end
  end
end

#factory_menu_bar(name, options = {}, &block) ⇒ Object

menu stuff



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/simple_layout.rb', line 268

def factory_menu_bar(name, options = {}, &block)
  cb = Proc.new do |id, w|
    id = id.gsub('_', '') if id.is_a?(String)
    m = "menu_#{name}_on_active"
    self.send(m, id, Gtk::ItemFactory.path_from_widget(w), w) if self.respond_to?(m)
  end
  @item_factory_stack ||= []
  @item_factory_stack.push [cb, [], []]
  block.call(name) if block
  options[:id] ||= name.to_sym
  _, _, items = @item_factory_stack.pop
  accel_group = Gtk::AccelGroup.new
  add_accel_group(accel_group)
  fact = Gtk::ItemFactory.new(Gtk::ItemFactory::TYPE_MENU_BAR, "<#{name}>", accel_group)
  fact.create_items(items)
  
  # process item attributes
  items.each do |x|
    # TODO: ...
  end
  layout_component(fact.get_widget("<#{name}>"), [], options)
end

#factory_menu_item(name, options = {}, &block) ⇒ Object



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
# File 'lib/simple_layout.rb', line 291

def factory_menu_item(name, options = {}, &block)
  cb, stack, items = @item_factory_stack.last
  branch = false
  options[:type] ||= :Item
  case name
  when /^[-]+$/
    options[:type] = :Separator
  when /^<[-]+>$/
    options[:type] = :Tearoff
  when /^>>(.+)>>$/
    name = $1
    branch = true
    options[:type] = :LastBranch
  when /^<(.+)>$/
    name = $1
    branch = true
    options[:type] = :Branch
  end

  image = options.delete(:image)
  if image.is_a?(String)
    options[:type] = :ImageItem
    image = Gdk::Pixbuf.new(image)
  elsif image.is_a?(Gdk::Pixbuf)
    options[:type] = :ImageItem
  elsif image
    options[:type] = :StockItem
  end

  item = [  "#{stack.last}/#{name}",
              "<#{options[:type].to_s}>",
              options[:accel],
              image,
              cb,
              options[:id] || name
            ]
  items << item
  if branch
    stack.push "#{stack.last}/#{name}"
    block.call(name) if block
    stack.pop if branch
  end
  item
end

#grid(left, top, *args, &block) ⇒ Object



262
263
264
265
# File 'lib/simple_layout.rb', line 262

def grid(left, top, *args, &block)
  args.push block
  container_pass_on(Gtk::Table, 'attach', left, left + 1, top, top + 1, *args)
end

#grid_flx(left, right, top, bottom, *args, &block) ⇒ Object

for Table container



258
259
260
261
# File 'lib/simple_layout.rb', line 258

def grid_flx(left, right, top, bottom, *args, &block)
  args.push block
  container_pass_on(Gtk::Table, 'attach', left, right, top, bottom, *args)
end

#group(name) {|cnt| ... } ⇒ Object

group the children

Yields:

  • (cnt)


226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/simple_layout.rb', line 226

def group(name)
  cnt, misc = @containers.last
  gs = (name ? [name].flatten : [])
  gs.each{|g| @component_children[g] ||= [] }
  m = { :groups => gs,
        :virtual => true,
        :sibling => misc[:sibling],
        :insp => misc[:insp],
        :layout => misc[:layout],
        :options => misc[:options],
        :accel_group => misc[:accel_group],
        :name => nil,
      }
  @containers.push [cnt, m]
  yield cnt if block_given?
  @containers.pop
end

#layout_component(w, args, options = {}, &block) ⇒ Object

layout the new UI component (container or widget) w: the widget args: the arguments for creating the widget (just for inspection purpose) options: the options for layout the widget block: the block for creating the children



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
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
418
419
# File 'lib/simple_layout.rb', line 341

def layout_component(w, args, options = {}, &block)
  @containers ||= []
  @pass_on_stack ||= []
  @components ||= {}
  @common_attribute ||= []
  @component_children ||= {}

  add_singleton_event_map(w) # so that you can use: w.on_clicked{|*args| ... }

  # there are some special options: :id, :gid, :layout
  # :id is the name of the component, :gid is the group name of the component, if :gid is not given, use :id as group name
  # :layout is the layout options for the component
  name = options.delete(:id)
  group_name = options.delete(:gid) || name
  layout_opt = options.delete(:layout)
  accel_group = options.delete(:accel_group)
  accel = options.delete(:accel)

  # the rest of the key-value pairs are turn into the function calls if the widget response to the key
  options.each do |k, v|
    if v.is_a?(Array)   # e.g. :set_size_request => [100, 100] turn into w.set_size_request(100, 100)
      w.send(k.to_s, *v) if w.respond_to?(k.to_s)
    else                # e.g. :size => 10 turn into w.size = 10
      w.send(k.to_s + '=', v) if w.respond_to?(k.to_s + '=')
    end
  end

  @components[name] = w if name   # add the widget to the components hash, if 'name' is given

  # if :gid is given, create the group if the group is not exist
  gs = (group_name ? [group_name].flatten : [])
  gs.each{|g| @component_children[g] ||= [] }

  parent, param = nil, nil
  if @containers.size > 0
    parent, param = @containers.last
    param[:groups].each{ |g| @component_children[g].push w }   # add the widget to the parent's children group
    param[:sibling] += 1    # record the sibling count

    # if the widget is a menuitem, add the accelerator to the menuitem
    menuitem_add_accel_group(w, accel, param[:accel_group]) if accel && param[:accel_group]

  end

  # if parent is a ScrolledWindow, create the inspector eventbox around the widget
  insp_evb = nil
  unless parent and parent.is_a?(Gtk::ScrolledWindow)
    insp_evb = make_inspect_evb(param, w, name, args, layout_opt, options)
  end

  if block # if given block, it's a container

    # if the widget options has :accel_group (a menu widget), create a new accelerator group to the container(menu)
    @components[accel_group] = Gtk::AccelGroup.new if accel_group
    m = { :groups => gs,
          :sibling => 0,
          :insp => insp_evb,
          :name => name,
          :layout => layout_opt,
          :options => options,
          :args => args,
          :accel_group => accel_group,
        }
    @containers.push [w, m] # push the new container to the stack
    @pass_on_stack.push [false, nil]
    @common_attribute.push({})
    block.call(w) # create the children
    @common_attribute.pop
    @pass_on_stack.pop
    @containers.pop
  end

  if @containers.size > 0
    add_component(insp_evb || w, parent, layout_opt) # add myself to parent
  else
    @components[:self] = self  # add host as ':self'
  end
  insp_evb || w
end

#page(text = nil, &block) ⇒ Object

for Notebook container



253
254
255
# File 'lib/simple_layout.rb', line 253

def page(text = nil, &block)
  container_pass_on(Gtk::Notebook, 'append_page', Gtk::Label.new(text), block)
end

#register_auto_eventsObject

register automatic event handlers



125
126
127
128
129
130
131
132
133
# File 'lib/simple_layout.rb', line 125

def register_auto_events()
  self.methods.each do |name|
    if name =~ /^(.+)_on_(.+)$/
      w, evt = $1, $2
      w = component(w.to_sym)
      w.signal_connect(evt) do |*args| self.send(name, *args) end if w
    end
  end
end

#with_attr(options = {}, &block) ⇒ Object

create a “with block” for setup common attributes



204
205
206
207
208
209
210
211
212
# File 'lib/simple_layout.rb', line 204

def with_attr(options = {}, &block)
  if block
    @common_attribute ||= []
    @common_attribute.push options
    cnt, _ = @containers.last
    block.call(cnt)
    @common_attribute.pop
  end
end