hotcocoa 0.5

Sign up to get free protection for your applications and to get access to all the features.
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