hotcocoa 0.5

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.
Files changed (84) hide show
  1. data/History.txt +4 -0
  2. data/bin/hotcocoa +31 -0
  3. data/lib/hotcocoa/application_builder.rb +320 -0
  4. data/lib/hotcocoa/attributed_string.rb +143 -0
  5. data/lib/hotcocoa/behaviors.rb +7 -0
  6. data/lib/hotcocoa/data_sources/combo_box_data_source.rb +44 -0
  7. data/lib/hotcocoa/data_sources/table_data_source.rb +18 -0
  8. data/lib/hotcocoa/delegate_builder.rb +85 -0
  9. data/lib/hotcocoa/graphics/canvas.rb +836 -0
  10. data/lib/hotcocoa/graphics/color.rb +781 -0
  11. data/lib/hotcocoa/graphics/elements/particle.rb +75 -0
  12. data/lib/hotcocoa/graphics/elements/rope.rb +99 -0
  13. data/lib/hotcocoa/graphics/elements/sandpainter.rb +71 -0
  14. data/lib/hotcocoa/graphics/gradient.rb +63 -0
  15. data/lib/hotcocoa/graphics/image.rb +488 -0
  16. data/lib/hotcocoa/graphics/path.rb +325 -0
  17. data/lib/hotcocoa/graphics/pdf.rb +71 -0
  18. data/lib/hotcocoa/graphics.rb +161 -0
  19. data/lib/hotcocoa/kernel_ext.rb +14 -0
  20. data/lib/hotcocoa/kvo_accessors.rb +48 -0
  21. data/lib/hotcocoa/layout_view.rb +448 -0
  22. data/lib/hotcocoa/mapper.rb +227 -0
  23. data/lib/hotcocoa/mapping_methods.rb +40 -0
  24. data/lib/hotcocoa/mappings/alert.rb +25 -0
  25. data/lib/hotcocoa/mappings/application.rb +112 -0
  26. data/lib/hotcocoa/mappings/array_controller.rb +87 -0
  27. data/lib/hotcocoa/mappings/box.rb +39 -0
  28. data/lib/hotcocoa/mappings/button.rb +92 -0
  29. data/lib/hotcocoa/mappings/collection_view.rb +44 -0
  30. data/lib/hotcocoa/mappings/color.rb +28 -0
  31. data/lib/hotcocoa/mappings/column.rb +21 -0
  32. data/lib/hotcocoa/mappings/combo_box.rb +24 -0
  33. data/lib/hotcocoa/mappings/control.rb +33 -0
  34. data/lib/hotcocoa/mappings/font.rb +44 -0
  35. data/lib/hotcocoa/mappings/gradient.rb +15 -0
  36. data/lib/hotcocoa/mappings/image.rb +15 -0
  37. data/lib/hotcocoa/mappings/image_view.rb +43 -0
  38. data/lib/hotcocoa/mappings/label.rb +25 -0
  39. data/lib/hotcocoa/mappings/layout_view.rb +9 -0
  40. data/lib/hotcocoa/mappings/menu.rb +71 -0
  41. data/lib/hotcocoa/mappings/menu_item.rb +47 -0
  42. data/lib/hotcocoa/mappings/movie.rb +13 -0
  43. data/lib/hotcocoa/mappings/movie_view.rb +27 -0
  44. data/lib/hotcocoa/mappings/notification.rb +17 -0
  45. data/lib/hotcocoa/mappings/popup.rb +110 -0
  46. data/lib/hotcocoa/mappings/progress_indicator.rb +68 -0
  47. data/lib/hotcocoa/mappings/scroll_view.rb +29 -0
  48. data/lib/hotcocoa/mappings/search_field.rb +9 -0
  49. data/lib/hotcocoa/mappings/secure_text_field.rb +17 -0
  50. data/lib/hotcocoa/mappings/segmented_control.rb +97 -0
  51. data/lib/hotcocoa/mappings/slider.rb +25 -0
  52. data/lib/hotcocoa/mappings/sort_descriptor.rb +13 -0
  53. data/lib/hotcocoa/mappings/sound.rb +9 -0
  54. data/lib/hotcocoa/mappings/speech_synthesizer.rb +25 -0
  55. data/lib/hotcocoa/mappings/split_view.rb +21 -0
  56. data/lib/hotcocoa/mappings/status_bar.rb +7 -0
  57. data/lib/hotcocoa/mappings/status_item.rb +9 -0
  58. data/lib/hotcocoa/mappings/table_view.rb +110 -0
  59. data/lib/hotcocoa/mappings/text_field.rb +41 -0
  60. data/lib/hotcocoa/mappings/text_view.rb +13 -0
  61. data/lib/hotcocoa/mappings/timer.rb +25 -0
  62. data/lib/hotcocoa/mappings/toolbar.rb +97 -0
  63. data/lib/hotcocoa/mappings/toolbar_item.rb +36 -0
  64. data/lib/hotcocoa/mappings/view.rb +67 -0
  65. data/lib/hotcocoa/mappings/web_view.rb +22 -0
  66. data/lib/hotcocoa/mappings/window.rb +118 -0
  67. data/lib/hotcocoa/mappings/xml_parser.rb +41 -0
  68. data/lib/hotcocoa/mappings.rb +109 -0
  69. data/lib/hotcocoa/mvc.rb +175 -0
  70. data/lib/hotcocoa/notification_listener.rb +62 -0
  71. data/lib/hotcocoa/object_ext.rb +22 -0
  72. data/lib/hotcocoa/plist.rb +45 -0
  73. data/lib/hotcocoa/standard_rake_tasks.rb +17 -0
  74. data/lib/hotcocoa/template.rb +27 -0
  75. data/lib/hotcocoa/virtual_file_system.rb +172 -0
  76. data/lib/hotcocoa.rb +26 -0
  77. data/template/Rakefile +5 -0
  78. data/template/config/build.yml +8 -0
  79. data/template/lib/application.rb +45 -0
  80. data/template/lib/menu.rb +32 -0
  81. data/template/resources/HotCocoa.icns +0 -0
  82. data/test/test_helper.rb +3 -0
  83. data/test/test_hotcocoa.rb +11 -0
  84. metadata +137 -0
@@ -0,0 +1,448 @@
1
+ module HotCocoa
2
+
3
+ module LayoutManaged
4
+
5
+ def layout=(options)
6
+ @layout = LayoutOptions.new(self, options)
7
+ @layout.update_layout_views!
8
+ end
9
+
10
+ def layout
11
+ @layout
12
+ end
13
+
14
+ end
15
+
16
+ class LayoutOptions
17
+
18
+ VALID_EXPANSIONS = [nil, :height, :width, [:height, :width], [:width, :height]]
19
+
20
+ attr_accessor :defaults_view
21
+ attr_reader :view
22
+
23
+ # options can be
24
+ #
25
+ # :start -> bool
26
+ # Whether the view is packed at the start or the end of the packing view.
27
+ # Default value is true.
28
+ #
29
+ # :expand -> :height, :width, [:height, :width]
30
+ # Whether the view's first dimension (width for horizontal and height for vertical)
31
+ # should be expanded to the maximum possible size, and should be variable according
32
+ # to the packing view frame.
33
+ # Default value is nil.
34
+ #
35
+ # :padding -> float
36
+ # :left_padding -> float
37
+ # :right_padding -> float
38
+ # :top_padding -> float
39
+ # :bottom_padding -> float
40
+ # Controls the padding area around the view. :padding controls all areas, while
41
+ # :left_padding for example only controls the left side. If :padding is set, other
42
+ # padding flags are ignored.
43
+ # Default value is 0.0 for all flags.
44
+ #
45
+ # :align -> mode
46
+ # Controls the view's alignment if its not expanded in the other dimension
47
+ # Modes can be:
48
+ # :left
49
+ # For horizontal layouts, align left
50
+ # :center
51
+ # Align center for horizontal or vertical layouts
52
+ # :right
53
+ # For horizontal layouts, align right
54
+ # :top
55
+ # For vertical layouts, align top
56
+ # :bottom
57
+ # For vertical layouts, align bottom
58
+ def initialize(view, options={})
59
+ @view = view
60
+ @start = options[:start]
61
+ @expand = options[:expand]
62
+ @padding = options[:padding]
63
+ @left_padding = @padding || options[:left_padding]
64
+ @right_padding = @padding || options[:right_padding]
65
+ @top_padding = @padding || options[:top_padding]
66
+ @bottom_padding = @padding || options[:bottom_padding]
67
+ @align = options[:align]
68
+ @defaults_view = options[:defaults_view]
69
+ end
70
+
71
+ def start=(value)
72
+ return if value == @start
73
+ @start = value
74
+ update_layout_views!
75
+ end
76
+
77
+ def start?
78
+ return @start unless @start.nil?
79
+ if in_layout_view?
80
+ @view.superview.default_layout.start?
81
+ else
82
+ true
83
+ end
84
+ end
85
+
86
+ def expand=(value)
87
+ return if value == @expand
88
+ unless VALID_EXPANSIONS.include?(value)
89
+ raise ArgumentError, "Expand must be nil, :height, :width or [:width, :height] not #{value.inspect}"
90
+ end
91
+ @expand = value
92
+ update_layout_views!
93
+ end
94
+
95
+ def expand
96
+ return @expand unless @expand.nil?
97
+ if in_layout_view?
98
+ @view.superview.default_layout.expand
99
+ else
100
+ false
101
+ end
102
+ end
103
+
104
+ def expand_width?
105
+ e = self.expand
106
+ e == :width || (e.respond_to?(:include?) && e.include?(:width))
107
+ end
108
+
109
+ def expand_height?
110
+ e = self.expand
111
+ e == :height || (e.respond_to?(:include?) && e.include?(:height))
112
+ end
113
+
114
+ def left_padding=(value)
115
+ return if value == @left_padding
116
+ @left_padding = value
117
+ @padding = nil
118
+ update_layout_views!
119
+ end
120
+
121
+ def left_padding
122
+ return @left_padding unless @left_padding.nil?
123
+ if in_layout_view?
124
+ @view.superview.default_layout.left_padding
125
+ else
126
+ @padding || 0.0
127
+ end
128
+ end
129
+
130
+ def right_padding=(value)
131
+ return if value == @right_padding
132
+ @right_padding = value
133
+ @padding = nil
134
+ update_layout_views!
135
+ end
136
+
137
+ def right_padding
138
+ return @right_padding unless @right_padding.nil?
139
+ if in_layout_view?
140
+ @view.superview.default_layout.right_padding
141
+ else
142
+ @padding || 0.0
143
+ end
144
+ end
145
+
146
+ def top_padding=(value)
147
+ return if value == @top_padding
148
+ @top_padding = value
149
+ @padding = nil
150
+ update_layout_views!
151
+ end
152
+
153
+ def top_padding
154
+ return @top_padding unless @top_padding.nil?
155
+ if in_layout_view?
156
+ @view.superview.default_layout.top_padding
157
+ else
158
+ @padding || 0.0
159
+ end
160
+ end
161
+
162
+ def bottom_padding=(value)
163
+ return if value == @bottom_padding
164
+ @bottom_padding = value
165
+ @padding = nil
166
+ update_layout_views!
167
+ end
168
+
169
+ def bottom_padding
170
+ return @bottom_padding unless @bottom_padding.nil?
171
+ if in_layout_view?
172
+ @view.superview.default_layout.bottom_padding
173
+ else
174
+ @padding || 0.0
175
+ end
176
+ end
177
+
178
+ def align
179
+ return @align unless @align.nil?
180
+ if in_layout_view?
181
+ @view.superview.default_layout.align
182
+ else
183
+ :left
184
+ end
185
+ end
186
+
187
+ def align=(value)
188
+ return if value == @align
189
+ @align = value
190
+ update_layout_views!
191
+ end
192
+
193
+ def padding=(value)
194
+ return if value == @padding
195
+ @right_padding = @left_padding = @top_padding = @bottom_padding = value
196
+ @padding = value
197
+ update_layout_views!
198
+ end
199
+
200
+ def padding
201
+ @padding
202
+ end
203
+
204
+ def inspect
205
+ "#<#{self.class} start=#{start?}, expand=#{expand.inspect}, left_padding=#{left_padding}, right_padding=#{right_padding}, top_padding=#{top_padding}, bottom_padding=#{bottom_padding}, align=#{align.inspect}, view=#{view.inspect}>"
206
+ end
207
+
208
+ def update_layout_views!
209
+ @view.superview.views_updated! if in_layout_view?
210
+ @defaults_view.views_updated! if @defaults_view
211
+ end
212
+
213
+ private
214
+
215
+ def in_layout_view?
216
+ @view && @view.superview.kind_of?(LayoutView)
217
+ end
218
+
219
+
220
+ end
221
+
222
+ class LayoutView < NSView
223
+
224
+ attr_accessor :frame_color
225
+
226
+ def initWithFrame(frame)
227
+ super
228
+ @mode = :vertical
229
+ @spacing = 10.0
230
+ @margin = 10.0
231
+ self
232
+ end
233
+
234
+ def vertical?
235
+ @mode == :vertical
236
+ end
237
+
238
+ def horizontal?
239
+ @mode == :horizonal
240
+ end
241
+
242
+ def mode=(mode)
243
+ if mode != :horizontal and mode != :vertical
244
+ raise ArgumentError, "invalid mode value #{mode}"
245
+ end
246
+ if mode != @mode
247
+ @mode = mode
248
+ relayout!
249
+ end
250
+ end
251
+
252
+ def default_layout=(options)
253
+ options[:defaults_view] = self
254
+ @default_layout = LayoutOptions.new(nil, options)
255
+ relayout!
256
+ end
257
+
258
+ def default_layout
259
+ @default_layout ||= LayoutOptions.new(nil, :defaults_view => self)
260
+ end
261
+
262
+ def spacing
263
+ @spacing
264
+ end
265
+
266
+ def spacing=(spacing)
267
+ if spacing != @spacing
268
+ @spacing = spacing.to_i
269
+ relayout!
270
+ end
271
+ end
272
+
273
+ def frame=(frame)
274
+ setFrame(frame)
275
+ end
276
+
277
+ def size=(size)
278
+ setFrameSize(size)
279
+ end
280
+
281
+ def margin
282
+ @margin
283
+ end
284
+
285
+ def margin=(margin)
286
+ if margin != @margin
287
+ @margin = margin.to_i
288
+ relayout!
289
+ end
290
+ end
291
+
292
+ def <<(view)
293
+ addSubview(view)
294
+ end
295
+
296
+ def remove(subview, options = {})
297
+ raise ArgumentError, "#{subview} is not a subview of #{self} and cannot be removed." unless subview.superview == self
298
+ options[:needs_display] == false ? subview.removeFromSuperviewWithoutNeedingDisplay : subview.removeFromSuperview
299
+ end
300
+
301
+ def addSubview(view)
302
+ super
303
+ if view.respond_to?(:layout)
304
+ relayout!
305
+ else
306
+ raise ArgumentError, "view #{view} does not support the #layout method"
307
+ end
308
+ end
309
+
310
+ def views_updated!
311
+ relayout!
312
+ end
313
+
314
+ def remove_view(view)
315
+ unless subviews.include?(view)
316
+ raise ArgumentError, "view #{view} not a subview of this LayoutView"
317
+ end
318
+ view.removeFromSuperview
319
+ relayout!
320
+ end
321
+
322
+ def remove_all_views
323
+ subviews.each { |view| view.removeFromSuperview }
324
+ relayout!
325
+ end
326
+
327
+ def drawRect(frame)
328
+ if @frame_color
329
+ @frame_color.set
330
+ NSFrameRect(frame)
331
+ end
332
+ end
333
+
334
+ def setFrame(frame)
335
+ super
336
+ relayout!
337
+ end
338
+
339
+ def setFrameSize(size)
340
+ super
341
+ relayout!
342
+ end
343
+
344
+ private
345
+
346
+ def relayout!
347
+ vertical = @mode == :vertical
348
+ view_size = frameSize
349
+ dimension = @margin
350
+ end_dimension = vertical ? view_size.height : view_size.width
351
+ end_dimension -= (@margin * 2)
352
+
353
+ expandable_size = end_dimension
354
+ expandable_views = 0
355
+ subviews.each do |view|
356
+ next if !view.respond_to?(:layout) || view.layout.nil?
357
+ if (vertical ? view.layout.expand_height? : view.layout.expand_width?)
358
+ expandable_views += 1
359
+ else
360
+ expandable_size -= vertical ? view.frameSize.height : view.frameSize.width
361
+ expandable_size -= @spacing
362
+ end
363
+ expandable_size -=
364
+ vertical ? view.layout.top_padding + view.layout.bottom_padding
365
+ : view.layout.left_padding + view.layout.right_padding
366
+ end
367
+ expandable_size /= expandable_views
368
+
369
+ subviews.each do |view|
370
+ next if !view.respond_to?(:layout) || view.layout.nil?
371
+ options = view.layout
372
+ subview_size = view.frameSize
373
+ view_frame = NSMakeRect(0, 0, *subview_size)
374
+ subview_dimension = vertical ? subview_size.height : subview_size.width
375
+
376
+ if vertical
377
+ view_frame.origin.x = @margin
378
+ if options.start?
379
+ view_frame.origin.y = dimension
380
+ else
381
+ view_frame.origin.y = end_dimension - subview_dimension
382
+ end
383
+ else
384
+ if options.start?
385
+ view_frame.origin.x = dimension
386
+ else
387
+ view_frame.origin.x = end_dimension - subview_dimension
388
+ end
389
+ view_frame.origin.y = @margin
390
+ end
391
+
392
+ if (vertical ? options.expand_height? : options.expand_width?)
393
+ if vertical
394
+ view_frame.size.height = expandable_size
395
+ else
396
+ view_frame.size.width = expandable_size
397
+ end
398
+ subview_dimension = expandable_size
399
+ end
400
+
401
+ if (vertical ? options.expand_width? : options.expand_height?)
402
+ if vertical
403
+ view_frame.size.width = view_size.width - (2 * @margin) - options.right_padding - options.left_padding
404
+ else
405
+ view_frame.size.height = view_size.height - (2 * @margin) - options.top_padding - options.bottom_padding
406
+ end
407
+ else
408
+ case options.align
409
+ when :left, :bottom
410
+ # Nothing to do
411
+
412
+ when :center
413
+ if vertical
414
+ view_frame.origin.x = (view_size.width / 2.0) - (subview_size.width / 2.0)
415
+ else
416
+ view_frame.origin.y = (view_size.height / 2.0) - (subview_size.height / 2.0)
417
+ end
418
+
419
+ when :right, :top
420
+ if vertical
421
+ view_frame.origin.x = view_size.width - subview_size.width - @margin
422
+ else
423
+ view_frame.origin.y = view_size.height - subview_size.height - @margin
424
+ end
425
+ end
426
+ end
427
+
428
+ puts "view #{view} options #{options} final frame #{view_frame}" if $DEBUG
429
+
430
+ view_frame.origin.x += options.left_padding
431
+ view_frame.origin.y += options.bottom_padding
432
+
433
+ if options.start?
434
+ dimension += subview_dimension + @spacing
435
+ if vertical
436
+ dimension += options.bottom_padding + options.top_padding
437
+ else
438
+ dimension += options.left_padding + options.right_padding
439
+ end
440
+ else
441
+ end_dimension -= subview_dimension + @spacing
442
+ end
443
+
444
+ view.frame = view_frame
445
+ end
446
+ end
447
+ end
448
+ end
@@ -0,0 +1,227 @@
1
+ module HotCocoa
2
+ module Mappings
3
+ class Mapper
4
+
5
+ attr_reader :control_class, :builder_method, :control_module
6
+
7
+ attr_accessor :map_bindings
8
+
9
+ def self.map_class(klass)
10
+ new(klass).include_in_class
11
+ end
12
+
13
+ def self.map_instances_of(klass, builder_method, &block)
14
+ new(klass).map_method(builder_method, &block)
15
+ end
16
+
17
+ def self.bindings_modules
18
+ @bindings_module ||= {}
19
+ end
20
+
21
+ def self.delegate_modules
22
+ @delegate_modules ||= {}
23
+ end
24
+
25
+ def initialize(klass)
26
+ @control_class = klass
27
+ end
28
+
29
+ def include_in_class
30
+ @extension_method = :include
31
+ customize(@control_class)
32
+ end
33
+
34
+ def map_method(builder_method, &block)
35
+ @extension_method = :extend
36
+ @builder_method = builder_method
37
+ mod = (class << self; self; end)
38
+ mod.extend MappingMethods
39
+ mod.module_eval &block
40
+ @control_module = mod
41
+ inst = self
42
+ HotCocoa.send(:define_method, builder_method) do |*args, &control_block|
43
+ map = (args.length == 1 ? args[0] : args[1]) || {}
44
+ guid = args.length == 1 ? nil : args[0]
45
+ map = inst.remap_constants(map)
46
+ inst.map_bindings = map.delete(:map_bindings)
47
+ default_empty_rect_used = (map[:frame].__id__ == DefaultEmptyRect.__id__)
48
+ control = inst.respond_to?(:init_with_options) ? inst.init_with_options(inst.control_class.alloc, map) : inst.alloc_with_options(map)
49
+ Views[guid] = control if guid
50
+ inst.customize(control)
51
+ map.each do |key, value|
52
+ if control.respond_to?("#{key}=")
53
+ eval "control.#{key} = value"
54
+ elsif control.respond_to?(key)
55
+ new_key = (key.start_with?('set') ? key : "set#{key[0].capitalize}#{key[1..(key.length - 1)]}")
56
+ if control.respond_to?(new_key)
57
+ eval "control.#{new_key}(value)"
58
+ else
59
+ control.send("#{key}")
60
+ end
61
+ elsif control.respond_to?("set#{Mapper.camel_case(key.to_s)}")
62
+ eval "control.set#{Mapper.camel_case(key.to_s)}(value)"
63
+ else
64
+ NSLog "Unable to map #{key} as a method"
65
+ end
66
+ end
67
+ if default_empty_rect_used
68
+ control.sizeToFit if control.respondsToSelector(:sizeToFit) == true
69
+ end
70
+ if control_block
71
+ if inst.respond_to?(:handle_block)
72
+ inst.handle_block(control, &control_block)
73
+ else
74
+ control_block.call(control)
75
+ end
76
+ end
77
+ control
78
+ end
79
+ # make the function callable using HotCocoa.xxxx
80
+ HotCocoa.send(:module_function, builder_method)
81
+ # module_function makes the instance method private, but we want it to stay public
82
+ HotCocoa.send(:public, builder_method)
83
+ self
84
+ end
85
+
86
+ def inherited_constants
87
+ constants = {}
88
+ each_control_ancestor do |ancestor|
89
+ constants = constants.merge(ancestor.control_module.constants_map)
90
+ end
91
+ constants
92
+ end
93
+
94
+ def inherited_delegate_methods
95
+ delegate_methods = {}
96
+ each_control_ancestor do |ancestor|
97
+ delegate_methods = delegate_methods.merge(ancestor.control_module.delegate_map)
98
+ end
99
+ delegate_methods
100
+ end
101
+
102
+ def inherited_custom_methods
103
+ methods = []
104
+ each_control_ancestor do |ancestor|
105
+ methods << ancestor.control_module.custom_methods if ancestor.control_module.custom_methods
106
+ end
107
+ methods
108
+ end
109
+
110
+ def each_control_ancestor
111
+ control_class.ancestors.reverse.each do |ancestor|
112
+ Mappings.mappings.values.each do |mapper|
113
+ yield mapper if mapper.control_class == ancestor
114
+ end
115
+ end
116
+ end
117
+
118
+ def customize(control)
119
+ inherited_custom_methods.each do |custom_methods|
120
+ control.send(@extension_method, custom_methods)
121
+ end
122
+ decorate_with_delegate_methods(control)
123
+ decorate_with_bindings_methods(control)
124
+ end
125
+
126
+ def decorate_with_delegate_methods(control)
127
+ control.send(@extension_method, delegate_module_for_control_class)
128
+ end
129
+
130
+ def delegate_module_for_control_class
131
+ unless Mapper.delegate_modules.has_key?(control_class)
132
+ delegate_module = Module.new
133
+ required_methods = []
134
+ delegate_methods = inherited_delegate_methods
135
+ if delegate_methods.size > 0
136
+ delegate_methods.each do |delegate_method, mapping|
137
+ required_methods << delegate_method if mapping[:required]
138
+ end
139
+ delegate_methods.each do |delegate_method, mapping|
140
+ parameters = mapping[:parameters] ? ", "+mapping[:parameters].map {|param| %{"#{param}"} }.join(",") : ""
141
+ delegate_module.module_eval %{
142
+ def #{mapping[:to]}(&block)
143
+ raise "Must pass in a block to use this delegate method" unless block_given?
144
+ @_delegate_builder ||= HotCocoa::DelegateBuilder.new(self, #{required_methods.inspect})
145
+ @_delegate_builder.add_delegated_method(block, "#{delegate_method}" #{parameters})
146
+ end
147
+ }
148
+ end
149
+ delegate_module.module_eval %{
150
+ def delegate_to(object)
151
+ @_delegate_builder ||= HotCocoa::DelegateBuilder.new(self, #{required_methods.inspect})
152
+ @_delegate_builder.delegate_to(object, #{delegate_methods.values.map {|method| ":#{method[:to]}"}.join(', ')})
153
+ end
154
+ }
155
+ end
156
+ Mapper.delegate_modules[control_class] = delegate_module
157
+ end
158
+ Mapper.delegate_modules[control_class]
159
+ end
160
+
161
+ def decorate_with_bindings_methods(control)
162
+ return if control_class == NSApplication
163
+ control.send(@extension_method, bindings_module_for_control(control)) if @map_bindings
164
+ end
165
+
166
+ def bindings_module_for_control(control)
167
+ return Mapper.bindings_modules[control_class] if Mapper.bindings_modules.has_key?(control_class)
168
+ instance = if control == control_class
169
+ control_class.alloc.init
170
+ else
171
+ control
172
+ end
173
+ bindings_module = Module.new
174
+ instance.exposedBindings.each do |exposed_binding|
175
+ bindings_module.module_eval %{
176
+ def #{Mapper.underscore(exposed_binding)}=(value)
177
+ if value.kind_of?(Hash)
178
+ options = value.delete(:options)
179
+ bind "#{exposed_binding}", toObject:value.keys.first, withKeyPath:value.values.first, options:options
180
+ else
181
+ set#{exposed_binding.capitalize}(value)
182
+ end
183
+ end
184
+ }
185
+ end
186
+ Mapper.bindings_modules[control_class] = bindings_module
187
+ bindings_module
188
+ end
189
+
190
+ def remap_constants(tags)
191
+ constants = inherited_constants
192
+ if control_module.defaults
193
+ control_module.defaults.each do |key, value|
194
+ tags[key] = value unless tags.has_key?(key)
195
+ end
196
+ end
197
+ result = {}
198
+ tags.each do |tag, value|
199
+ if constants[tag]
200
+ result[tag] = value.kind_of?(Array) ? value.inject(0) {|a, i| a|constants[tag][i]} : constants[tag][value]
201
+ else
202
+ result[tag] = value
203
+ end
204
+ end
205
+ result
206
+ end
207
+
208
+ def self.underscore(string)
209
+ string.gsub(/::/, '/').
210
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
211
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
212
+ tr("-", "_").
213
+ downcase
214
+ end
215
+
216
+ def self.camel_case(string)
217
+ if string !~ /_/ && string =~ /[A-Z]+.*/
218
+ string
219
+ else
220
+ string.split('_').map{ |e| e.capitalize }.join
221
+ end
222
+ end
223
+
224
+
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,40 @@
1
+ module HotCocoa
2
+
3
+ module MappingMethods
4
+
5
+ def defaults(defaults=nil)
6
+ if defaults
7
+ @defaults = defaults
8
+ else
9
+ @defaults
10
+ end
11
+ end
12
+
13
+ def constant(name, constants)
14
+ constants_map[name] = constants
15
+ end
16
+
17
+ def constants_map
18
+ @constants_map ||= {}
19
+ end
20
+
21
+ def custom_methods(&block)
22
+ if block
23
+ @custom_methods = Module.new
24
+ @custom_methods.module_eval(&block)
25
+ else
26
+ @custom_methods
27
+ end
28
+ end
29
+
30
+ def delegating(name, options)
31
+ delegate_map[name] = options
32
+ end
33
+
34
+ def delegate_map
35
+ @delegate_map ||= {}
36
+ end
37
+
38
+ end
39
+
40
+ end