GtkSimpleLayout 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +339 -0
- data/README +47 -0
- data/Rakefile +18 -0
- data/example/auto_event_map.rb +60 -0
- data/example/basic.rb +36 -0
- data/example/calculator.rb +64 -0
- data/example/composit_layout.rb +26 -0
- data/example/expose_components.rb +35 -0
- data/example/group.rb +70 -0
- data/example/hello_world.rb +23 -0
- data/example/menu.rb +54 -0
- data/example/serial_port_setup.rb +85 -0
- data/example/with_attr.rb +44 -0
- data/lib/simple_layout.rb +488 -0
- metadata +78 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
require 'gtk2'
|
|
2
|
+
|
|
3
|
+
module SimpleLayout
|
|
4
|
+
|
|
5
|
+
class LayoutError < Exception; end
|
|
6
|
+
|
|
7
|
+
class EventHandlerProxy
|
|
8
|
+
def initialize(host, evt, &block)
|
|
9
|
+
@host = host
|
|
10
|
+
@evt = evt
|
|
11
|
+
self << block if block
|
|
12
|
+
end
|
|
13
|
+
def <<(v)
|
|
14
|
+
if v && v.respond_to?('call')
|
|
15
|
+
@host.signal_connect(@evt) do |*args|
|
|
16
|
+
v.call(*args)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module ExtClassMethod
|
|
24
|
+
def inspector_opt(opt = nil)
|
|
25
|
+
@insp_opt ||= {
|
|
26
|
+
:enable => (ENV['INSPECTOR_ENABLE'] == '1'),
|
|
27
|
+
:border_width => (ENV['INSPECTOR_BORDER_WIDTH'] || 5)
|
|
28
|
+
}
|
|
29
|
+
@insp_opt.merge! opt if opt
|
|
30
|
+
@insp_opt
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def layout_class_maps
|
|
34
|
+
@layout_class_maps_hash ||= {
|
|
35
|
+
'image' => Gtk::Image,
|
|
36
|
+
'label' => Gtk::Label,
|
|
37
|
+
'progress_bar' => Gtk::ProgressBar,
|
|
38
|
+
'status_bar' => Gtk::Statusbar,
|
|
39
|
+
'button' => Gtk::Button,
|
|
40
|
+
'check_button' => Gtk::CheckButton,
|
|
41
|
+
'radio_button' => Gtk::RadioButton,
|
|
42
|
+
'toggle_button' => Gtk::ToggleButton,
|
|
43
|
+
'link_button' => Gtk::LinkButton,
|
|
44
|
+
'entry' => Gtk::Entry,
|
|
45
|
+
'hscale' => Gtk::HScale,
|
|
46
|
+
'vscale' => Gtk::VScale,
|
|
47
|
+
'spin_button' => Gtk::SpinButton,
|
|
48
|
+
'text_view' => Gtk::TextView,
|
|
49
|
+
'tree_view' => Gtk::TreeView,
|
|
50
|
+
'cell_view' => Gtk::CellView,
|
|
51
|
+
'icon_view' => Gtk::IconView,
|
|
52
|
+
'combobox' => Gtk::ComboBox,
|
|
53
|
+
'combobox_entry' => Gtk::ComboBoxEntry,
|
|
54
|
+
#'menu' => Gtk::Menu,
|
|
55
|
+
#'menubar' => Gtk::MenuBar,
|
|
56
|
+
'toolbar' => Gtk::Toolbar,
|
|
57
|
+
'toolitem' => Gtk::ToolItem,
|
|
58
|
+
'separator_toolitem' => Gtk::SeparatorToolItem,
|
|
59
|
+
'tool_button' => Gtk::ToolButton,
|
|
60
|
+
'toggle_tool_button' => Gtk::ToggleToolButton,
|
|
61
|
+
'radio_tool_button' => Gtk::RadioToolButton,
|
|
62
|
+
'color_button' => Gtk::ColorButton,
|
|
63
|
+
'color_selection' => Gtk::ColorSelection,
|
|
64
|
+
'file_chooser_button' => Gtk::FileChooserButton,
|
|
65
|
+
'file_chooser_widget' => Gtk::FileChooserWidget,
|
|
66
|
+
'font_button' => Gtk::FontButton,
|
|
67
|
+
'font_selection' => Gtk::FontSelection,
|
|
68
|
+
'alignment' => Gtk::Alignment,
|
|
69
|
+
'aspect_frame' => Gtk::AspectFrame,
|
|
70
|
+
'hbox' => Gtk::HBox,
|
|
71
|
+
'vbox' => Gtk::VBox,
|
|
72
|
+
'hbutton_box' => Gtk::HButtonBox,
|
|
73
|
+
'vbutton_box' => Gtk::VButtonBox,
|
|
74
|
+
'hpaned' => Gtk::HPaned,
|
|
75
|
+
'vpaned' => Gtk::VPaned,
|
|
76
|
+
'layout' => Gtk::Layout,
|
|
77
|
+
'notebook' => Gtk::Notebook,
|
|
78
|
+
'table' => Gtk::Table,
|
|
79
|
+
'expander' => Gtk::Expander,
|
|
80
|
+
'frame' => Gtk::Frame,
|
|
81
|
+
'hseparator' => Gtk::HSeparator,
|
|
82
|
+
'vseparator' => Gtk::VSeparator,
|
|
83
|
+
'hscrollbar' => Gtk::HScrollbar,
|
|
84
|
+
'vscrollbar' => Gtk::VScrollbar,
|
|
85
|
+
'scrolled_window' => Gtk::ScrolledWindow,
|
|
86
|
+
'arrow' => Gtk::Arrow,
|
|
87
|
+
'calendar' => Gtk::Calendar,
|
|
88
|
+
'drawing_area' => Gtk::DrawingArea,
|
|
89
|
+
'event_box' => Gtk::EventBox,
|
|
90
|
+
'handle_box' => Gtk::HandleBox,
|
|
91
|
+
'viewport' => Gtk::Viewport,
|
|
92
|
+
'curve' => Gtk::Curve,
|
|
93
|
+
'gamma_curve' => Gtk::GammaCurve,
|
|
94
|
+
'hruler' => Gtk::HRuler,
|
|
95
|
+
'vruler' => Gtk::VRuler,
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
module Base
|
|
102
|
+
def Base.included(base)
|
|
103
|
+
base.extend(ExtClassMethod)
|
|
104
|
+
base.layout_class_maps.each do |k, v|
|
|
105
|
+
define_method(k) do |*args, &block|
|
|
106
|
+
create_component(v, args, block)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
public
|
|
112
|
+
|
|
113
|
+
# register automatic event handlers
|
|
114
|
+
def register_auto_events()
|
|
115
|
+
self.methods.each do |name|
|
|
116
|
+
if name =~ /^(.+)_on_(.+)$/
|
|
117
|
+
w, evt = $1, $2
|
|
118
|
+
w = component(w.to_sym)
|
|
119
|
+
w.signal_connect(evt) do |*args| self.send(name, *args) end if w
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# expose the components as instance variables
|
|
125
|
+
def expose_components()
|
|
126
|
+
@components.each_key do |k|
|
|
127
|
+
unless self.respond_to?(k.to_s, true)
|
|
128
|
+
self.instance_eval("def #{k.to_s}; component(:#{k.to_s}) end")
|
|
129
|
+
else
|
|
130
|
+
raise LayoutError, "#{k} is conflit with method, please redifine component id"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# add a widget to container (and/or become a new container as well).
|
|
136
|
+
# do not call this function directly unless knowing what you are doing
|
|
137
|
+
def add_component(w, container, layout_opt = nil)
|
|
138
|
+
if @pass_on_stack.last.nil? || @pass_on_stack.last[0] == false
|
|
139
|
+
if container.is_a?(Gtk::Box)
|
|
140
|
+
layout_opt ||= [false, false, 0]
|
|
141
|
+
pack_method = 'pack_start'
|
|
142
|
+
if layout_opt.first.is_a?(Symbol)
|
|
143
|
+
pack_method = 'pack_end' if layout_opt.shift == :end
|
|
144
|
+
end
|
|
145
|
+
container.send(pack_method, w, *layout_opt)
|
|
146
|
+
elsif container.is_a?(Gtk::Fixed) || container.is_a?(Gtk::Layout)
|
|
147
|
+
layout_opt ||= [0, 0]
|
|
148
|
+
container.put w, *layout_opt
|
|
149
|
+
elsif container.is_a?(Gtk::MenuShell)
|
|
150
|
+
container.append w
|
|
151
|
+
elsif container.is_a?(Gtk::Toolbar)
|
|
152
|
+
container.insert(container.n_items, w)
|
|
153
|
+
elsif container.is_a?(Gtk::MenuToolButton)
|
|
154
|
+
container.menu = w
|
|
155
|
+
elsif container.is_a?(Gtk::Table)
|
|
156
|
+
# should use #grid or #grid_flx to add a child to Table
|
|
157
|
+
elsif container.is_a?(Gtk::Notebook)
|
|
158
|
+
# should use #page to add a child to Notebook
|
|
159
|
+
elsif container.is_a?(Gtk::Paned)
|
|
160
|
+
# should use #area_first or #area_second to add child to Paned
|
|
161
|
+
elsif container.is_a?(Gtk::Container)
|
|
162
|
+
layout_opt ||= []
|
|
163
|
+
container.add(w, *layout_opt)
|
|
164
|
+
end
|
|
165
|
+
else
|
|
166
|
+
fun_name, args = *(@pass_on_stack.last[1])
|
|
167
|
+
container.send(fun_name, w, *args)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# create a "with block" for setup common attributes
|
|
172
|
+
def with_attr(options = {}, &block)
|
|
173
|
+
if block
|
|
174
|
+
@common_attribute ||= []
|
|
175
|
+
@common_attribute.push options
|
|
176
|
+
cnt, _ = @containers.last
|
|
177
|
+
block.call(cnt)
|
|
178
|
+
@common_attribute.pop
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# get component with given name
|
|
183
|
+
def component(name)
|
|
184
|
+
@components[name]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# return children array of a component or group
|
|
188
|
+
def component_children(name)
|
|
189
|
+
@component_children ||= {}
|
|
190
|
+
@component_children[name]
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# group the children
|
|
194
|
+
def group(name)
|
|
195
|
+
cnt, misc = @containers.last
|
|
196
|
+
gs = (name ? [name].flatten : [])
|
|
197
|
+
gs.each{|g| @component_children[g] ||= [] }
|
|
198
|
+
m = { :groups => gs,
|
|
199
|
+
:virtual => true,
|
|
200
|
+
:sibling => misc[:sibling],
|
|
201
|
+
:insp => misc[:insp],
|
|
202
|
+
:layout => misc[:layout],
|
|
203
|
+
:options => misc[:options],
|
|
204
|
+
:name => nil,
|
|
205
|
+
}
|
|
206
|
+
@containers.push [cnt, m]
|
|
207
|
+
yield cnt if block_given?
|
|
208
|
+
@containers.pop
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# for HPaned and VPaned container
|
|
212
|
+
def area_first(resize = true, shrink = true, &block)
|
|
213
|
+
container_pass_on(Gtk::Paned, 'pack1', resize, shrink, block)
|
|
214
|
+
end
|
|
215
|
+
def area_second(resize = true, shrink = true, &block)
|
|
216
|
+
container_pass_on(Gtk::Paned, 'pack2', resize, shrink, block)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# for Notebook container
|
|
220
|
+
def page(text = nil, &block)
|
|
221
|
+
container_pass_on(Gtk::Notebook, 'append_page', Gtk::Label.new(text), block)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# for Table container
|
|
225
|
+
def grid_flx(left, right, top, bottom, *args, &block)
|
|
226
|
+
args.push block
|
|
227
|
+
container_pass_on(Gtk::Table, 'attach', left, right, top, bottom, *args)
|
|
228
|
+
end
|
|
229
|
+
def grid(left, top, *args, &block)
|
|
230
|
+
args.push block
|
|
231
|
+
container_pass_on(Gtk::Table, 'attach', left, left + 1, top, top + 1, *args)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# menu stuff
|
|
235
|
+
def factory_menu_bar(name, options = {}, &block)
|
|
236
|
+
cb = Proc.new do |id, w|
|
|
237
|
+
id = id.gsub('_', '') if id.is_a?(String)
|
|
238
|
+
m = "menu_#{name}_on_active"
|
|
239
|
+
self.send(m, id, Gtk::ItemFactory.path_from_widget(w), w) if self.respond_to?(m)
|
|
240
|
+
end
|
|
241
|
+
@item_factory_stack ||= []
|
|
242
|
+
@item_factory_stack.push [cb, [], []]
|
|
243
|
+
block.call(name) if block
|
|
244
|
+
options[:id] ||= name.to_sym
|
|
245
|
+
_, _, items = @item_factory_stack.pop
|
|
246
|
+
accel_group = Gtk::AccelGroup.new
|
|
247
|
+
add_accel_group(accel_group)
|
|
248
|
+
fact = Gtk::ItemFactory.new(Gtk::ItemFactory::TYPE_MENU_BAR, "<#{name}>", accel_group)
|
|
249
|
+
fact.create_items(items)
|
|
250
|
+
|
|
251
|
+
# process item attributes
|
|
252
|
+
items.each do |x|
|
|
253
|
+
# TODO: ...
|
|
254
|
+
end
|
|
255
|
+
layout_component(fact.get_widget("<#{name}>"), options)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def factory_menu_item(name, options = {}, &block)
|
|
259
|
+
cb, stack, items = @item_factory_stack.last
|
|
260
|
+
branch = false
|
|
261
|
+
options[:type] ||= :Item
|
|
262
|
+
case name
|
|
263
|
+
when /^[-]+$/
|
|
264
|
+
options[:type] = :Separator
|
|
265
|
+
when /^<[-]+>$/
|
|
266
|
+
options[:type] = :Tearoff
|
|
267
|
+
when /^>>(.+)>>$/
|
|
268
|
+
name = $1
|
|
269
|
+
branch = true
|
|
270
|
+
options[:type] = :LastBranch
|
|
271
|
+
when /^<(.+)>$/
|
|
272
|
+
name = $1
|
|
273
|
+
branch = true
|
|
274
|
+
options[:type] = :Branch
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
image = options.delete(:image)
|
|
278
|
+
if image.is_a?(String)
|
|
279
|
+
options[:type] = :ImageItem
|
|
280
|
+
image = Gdk::Pixbuf.new(image)
|
|
281
|
+
elsif image.is_a?(Gdk::Pixbuf)
|
|
282
|
+
options[:type] = :ImageItem
|
|
283
|
+
elsif image
|
|
284
|
+
options[:type] = :StockItem
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
item = [ "#{stack.last}/#{name}",
|
|
288
|
+
"<#{options[:type].to_s}>",
|
|
289
|
+
options[:accel],
|
|
290
|
+
image,
|
|
291
|
+
cb,
|
|
292
|
+
options[:id] || name
|
|
293
|
+
]
|
|
294
|
+
items << item
|
|
295
|
+
if branch
|
|
296
|
+
stack.push "#{stack.last}/#{name}"
|
|
297
|
+
block.call(name) if block
|
|
298
|
+
stack.pop if branch
|
|
299
|
+
end
|
|
300
|
+
item
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# layout the new UI component (container or widget)
|
|
304
|
+
def layout_component(w, options = {}, &block)
|
|
305
|
+
@containers ||= []
|
|
306
|
+
@pass_on_stack ||= []
|
|
307
|
+
@components ||= {}
|
|
308
|
+
@common_attribute ||= []
|
|
309
|
+
@component_children ||= {}
|
|
310
|
+
|
|
311
|
+
add_singleton_event_map(w) # so that you can use: w.on_clicked{|*args| ... }
|
|
312
|
+
|
|
313
|
+
name = options.delete(:id)
|
|
314
|
+
group_name = options.delete(:gid) || name
|
|
315
|
+
layout_opt = options.delete(:layout)
|
|
316
|
+
keep_top_cnt = options.delete(:keep_top_container)
|
|
317
|
+
|
|
318
|
+
options.each do |k, v|
|
|
319
|
+
if v.is_a?(Array)
|
|
320
|
+
w.send(k.to_s, *v) if w.respond_to?(k.to_s)
|
|
321
|
+
else
|
|
322
|
+
w.send(k.to_s + '=', v) if w.respond_to?(k.to_s + '=')
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
@components[name] = w if name
|
|
327
|
+
gs = (group_name ? [group_name].flatten : [])
|
|
328
|
+
gs.each{|g| @component_children[g] ||= [] }
|
|
329
|
+
|
|
330
|
+
misc = nil
|
|
331
|
+
if @containers.size > 0
|
|
332
|
+
container, misc = @containers.last
|
|
333
|
+
misc[:groups].each{ |g| @component_children[g].push w }
|
|
334
|
+
misc[:sibling] += 1
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
unless container and container.is_a? Gtk::ScrolledWindow
|
|
338
|
+
insp_evb = make_inspect_evb(misc, w, name, layout_opt, options)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
if block # if given block, it's a container as well
|
|
342
|
+
m = { :groups => gs,
|
|
343
|
+
:sibling => 0,
|
|
344
|
+
:insp => insp_evb,
|
|
345
|
+
:name => name,
|
|
346
|
+
:layout => layout_opt,
|
|
347
|
+
:options => options,
|
|
348
|
+
}
|
|
349
|
+
@containers.push [w, m]
|
|
350
|
+
@pass_on_stack.push [false, nil]
|
|
351
|
+
@common_attribute.push({})
|
|
352
|
+
block.call(w) if block
|
|
353
|
+
@common_attribute.pop
|
|
354
|
+
@pass_on_stack.pop
|
|
355
|
+
@containers.pop
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
if @containers.size > 0
|
|
359
|
+
add_component(insp_evb || w, container, layout_opt) # add myself to parent
|
|
360
|
+
else
|
|
361
|
+
add_component(insp_evb || w, self, layout_opt) unless keep_top_cnt # add top container to host
|
|
362
|
+
@components[:self] = self # add host as ':self'
|
|
363
|
+
end
|
|
364
|
+
w
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
private
|
|
368
|
+
|
|
369
|
+
def add_singleton_event_map(w)
|
|
370
|
+
class << w
|
|
371
|
+
alias_method :simple_layout_singleton_method_missing, :method_missing
|
|
372
|
+
def method_missing(sym, *args, &block)
|
|
373
|
+
if sym.to_s =~ /^on_([^=]+)(=*)$/
|
|
374
|
+
block ||= args.last
|
|
375
|
+
return EventHandlerProxy.new(self, $1, &block)
|
|
376
|
+
else
|
|
377
|
+
simple_layout_singleton_method_missing(sym, *args, &block)
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# create the inspector eventbox for widget
|
|
384
|
+
def make_inspect_evb(cnt_misc, w, name, layout_opt, options)
|
|
385
|
+
insp_evb = nil
|
|
386
|
+
insp_opt = self.class.inspector_opt
|
|
387
|
+
if insp_opt[:enable]
|
|
388
|
+
rgb = 0xffff - @containers.size * 0x1000
|
|
389
|
+
insp_evb = evb = Gtk::EventBox.new
|
|
390
|
+
sub_evb = Gtk::EventBox.new
|
|
391
|
+
sub_evb.add w
|
|
392
|
+
evb.add sub_evb
|
|
393
|
+
sub_evb.border_width = insp_opt[:border_width]
|
|
394
|
+
evb.modify_bg Gtk::STATE_NORMAL, Gdk::Color.new(rgb, rgb, rgb)
|
|
395
|
+
evbs = []
|
|
396
|
+
tips = ""
|
|
397
|
+
@containers.size.times do |i|
|
|
398
|
+
cnt, m = @containers[i]
|
|
399
|
+
if m[:insp] && (not m[:virtual])
|
|
400
|
+
evbs << m[:insp]
|
|
401
|
+
tips << "<b>container[#{i}]: #{cnt.class}#{m[:name] ? " (#{m[:name]})" : ''}</b>\n"
|
|
402
|
+
tips << " layout: #{m[:layout].inspect}\n" if m[:layout]
|
|
403
|
+
tips << " options: #{m[:options].inspect}\n" if m[:options] && m[:options].size > 0
|
|
404
|
+
tips << " groups: #{m[:groups].inspect}\n" if m[:groups].size > 0
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
evbs << evb
|
|
408
|
+
tips << "<b>widget: #{w.class}#{name ? " (#{name})" : ''}</b>\n"
|
|
409
|
+
tips << " layout: #{layout_opt.inspect}\n" if layout_opt
|
|
410
|
+
tips << " options: #{options.inspect}\n" if options && options.size > 0
|
|
411
|
+
tips << " groups: #{cnt_misc[:groups].inspect}\n" if cnt_misc && cnt_misc[:groups].size > 0
|
|
412
|
+
|
|
413
|
+
evb.signal_connect('event') do |b, evt|
|
|
414
|
+
b.tooltip_markup = tips
|
|
415
|
+
case evt.event_type
|
|
416
|
+
when Gdk::Event::ENTER_NOTIFY, Gdk::Event::LEAVE_NOTIFY
|
|
417
|
+
evbs.size.times do |i|
|
|
418
|
+
rgb = 0xffff - i * 0x1000
|
|
419
|
+
if evt.event_type == Gdk::Event::ENTER_NOTIFY
|
|
420
|
+
evbs[i].modify_bg Gtk::STATE_NORMAL, Gdk::Color.new(rgb, rgb - 0x2000, rgb - 0x2000)
|
|
421
|
+
elsif evt.event_type == Gdk::Event::LEAVE_NOTIFY
|
|
422
|
+
evbs[i].modify_bg Gtk::STATE_NORMAL, Gdk::Color.new(rgb, rgb, rgb)
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
insp_evb
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
# create a new UI component (container or widget)
|
|
432
|
+
def create_component(component_class, args, block)
|
|
433
|
+
@common_attribute ||= []
|
|
434
|
+
options = {}
|
|
435
|
+
options = args.pop if args.last.is_a?(Hash)
|
|
436
|
+
options.merge! @common_attribute.last if @common_attribute.last
|
|
437
|
+
|
|
438
|
+
w = component_class.new(*args)
|
|
439
|
+
layout_component(w, options, &block)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def container_pass_on(container_class, fun_name, *args)
|
|
443
|
+
block = args.pop # the last arg is Proc or nil
|
|
444
|
+
cnt, _ = @containers.last
|
|
445
|
+
if cnt.is_a?(container_class)
|
|
446
|
+
@pass_on_stack.push [true, [fun_name, args]]
|
|
447
|
+
block.call(cnt) if block
|
|
448
|
+
@pass_on_stack.pop
|
|
449
|
+
else
|
|
450
|
+
raise LayoutError, "class #{container_class} expected"
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
alias_method :simple_layout_method_missing_alias, :method_missing
|
|
455
|
+
|
|
456
|
+
def method_missing(sym, *args, &block)
|
|
457
|
+
if sym.to_s =~ /^(.+)_in_(.+)$/
|
|
458
|
+
maps = self.class.layout_class_maps
|
|
459
|
+
inner, outter = $1, $2
|
|
460
|
+
if maps[inner] && maps[outter]
|
|
461
|
+
if args.last.is_a?(Hash)
|
|
462
|
+
options = {}
|
|
463
|
+
options = args.pop if args.last.is_a?(Hash)
|
|
464
|
+
|
|
465
|
+
# default args pass to inner component, execpt:
|
|
466
|
+
# :layout pass to outter :layout
|
|
467
|
+
# :inner_layout pass to inner :layout
|
|
468
|
+
# :outter_args pass to outter args
|
|
469
|
+
outter_args, outter_layout_opt, options[:layout] =
|
|
470
|
+
options.delete(:outter_args), options.delete(:layout), options.delete(:inner_layout)
|
|
471
|
+
|
|
472
|
+
outter_args = (outter_args ? [outter_args] : []) unless outter_args.is_a?(Array)
|
|
473
|
+
outter_args << {} unless outter_args.last.is_a?(Hash)
|
|
474
|
+
outter_args.last[:layout] ||= outter_layout_opt
|
|
475
|
+
args.push options # push back inner options
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
inner_proc = Proc.new do
|
|
479
|
+
create_component(maps[inner], args, block)
|
|
480
|
+
end
|
|
481
|
+
return create_component(maps[outter], outter_args || [], inner_proc)
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
simple_layout_method_missing_alias(sym, *args, &block)
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
end
|
|
488
|
+
end
|