Module: SimpleLayout::Base

Defined in:
lib/simple_layout.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



105
106
107
108
109
110
111
112
# File 'lib/simple_layout.rb', line 105

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



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

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



237
238
239
# File 'lib/simple_layout.rb', line 237

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



240
241
242
# File 'lib/simple_layout.rb', line 240

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



207
208
209
# File 'lib/simple_layout.rb', line 207

def component(name)
  @components[name]
end

#component_children(name) ⇒ Object

return children array of a component or group



212
213
214
215
# File 'lib/simple_layout.rb', line 212

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

#expose_componentsObject

expose the components as instance variables



128
129
130
131
132
133
134
135
136
# File 'lib/simple_layout.rb', line 128

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



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/simple_layout.rb', line 260

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



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

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



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

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



250
251
252
253
# File 'lib/simple_layout.rb', line 250

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)


218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/simple_layout.rb', line 218

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



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

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, :keep_top_container
  # :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, :keep_top_container is to keep the top container
  name = options.delete(:id)
  group_name = options.delete(:gid) || name
  layout_opt = options.delete(:layout)
  keep_top_cnt = options.delete(:keep_top_container)
  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
    add_component(insp_evb || w, self, layout_opt) unless keep_top_cnt # add top container to host
    @components[:self] = self  # add host as ':self'
  end
  w
end

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

for Notebook container



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

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



117
118
119
120
121
122
123
124
125
# File 'lib/simple_layout.rb', line 117

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



196
197
198
199
200
201
202
203
204
# File 'lib/simple_layout.rb', line 196

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