hotcocoa 0.5.1 → 0.6.0pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. data/.yardopts +14 -0
  2. data/History.txt +21 -0
  3. data/README.markdown +84 -0
  4. data/Rakefile +47 -0
  5. data/bin/hotcocoa +20 -10
  6. data/docs/Contributors.markdown +25 -0
  7. data/docs/Mappings.markdown +352 -0
  8. data/docs/MappingsExplained.markdown +22 -0
  9. data/docs/Overview.markdown +58 -0
  10. data/docs/Resources.markdown +25 -0
  11. data/docs/Tutorial.markdown +3 -0
  12. data/lib/hotcocoa/application/builder.rb +237 -0
  13. data/lib/hotcocoa/application/specification.rb +373 -0
  14. data/lib/hotcocoa/application_builder.rb +338 -242
  15. data/lib/hotcocoa/attributed_string_helpers.rb +128 -0
  16. data/lib/hotcocoa/behaviors.rb +39 -6
  17. data/lib/hotcocoa/core_extensions/kernel.rb +67 -0
  18. data/lib/hotcocoa/core_extensions/nsarray.rb +15 -0
  19. data/lib/hotcocoa/core_extensions/nsmutable_attributed_string.rb +74 -0
  20. data/lib/hotcocoa/core_extensions/nsstring.rb +36 -0
  21. data/lib/hotcocoa/core_extensions/nsurl.rb +6 -0
  22. data/lib/hotcocoa/core_extensions/numeric.rb +9 -0
  23. data/lib/hotcocoa/core_extensions/object.rb +36 -0
  24. data/lib/hotcocoa/core_extensions/range.rb +30 -0
  25. data/lib/hotcocoa/core_extensions.rb +8 -0
  26. data/lib/hotcocoa/data_sources/combo_box_data_source.rb +38 -38
  27. data/lib/hotcocoa/data_sources/outline_view_data_source.rb +33 -0
  28. data/lib/hotcocoa/data_sources/table_data_source.rb +12 -13
  29. data/lib/hotcocoa/data_sources.rb +3 -0
  30. data/lib/hotcocoa/delegate_builder.rb +114 -79
  31. data/lib/hotcocoa/graphics/canvas.rb +211 -214
  32. data/lib/hotcocoa/graphics/color.rb +89 -91
  33. data/lib/hotcocoa/graphics/elements/particle.rb +16 -19
  34. data/lib/hotcocoa/graphics/elements/rope.rb +12 -14
  35. data/lib/hotcocoa/graphics/elements/sandpainter.rb +11 -13
  36. data/lib/hotcocoa/graphics/gradient.rb +13 -14
  37. data/lib/hotcocoa/graphics/image.rb +110 -83
  38. data/lib/hotcocoa/graphics/path.rb +44 -50
  39. data/lib/hotcocoa/graphics/pdf.rb +17 -19
  40. data/lib/hotcocoa/graphics.rb +21 -20
  41. data/lib/hotcocoa/kvo_accessors.rb +10 -11
  42. data/lib/hotcocoa/layout_view.rb +394 -379
  43. data/lib/hotcocoa/mapper.rb +346 -207
  44. data/lib/hotcocoa/mapping_methods.rb +107 -37
  45. data/lib/hotcocoa/mappings/appkit/alert.rb +25 -0
  46. data/lib/hotcocoa/mappings/appkit/application.rb +112 -0
  47. data/lib/hotcocoa/mappings/{array_controller.rb → appkit/array_controller.rb} +33 -35
  48. data/lib/hotcocoa/mappings/appkit/box.rb +37 -0
  49. data/lib/hotcocoa/mappings/appkit/button.rb +88 -0
  50. data/lib/hotcocoa/mappings/appkit/collection_view.rb +42 -0
  51. data/lib/hotcocoa/mappings/appkit/color.rb +40 -0
  52. data/lib/hotcocoa/mappings/appkit/column.rb +19 -0
  53. data/lib/hotcocoa/mappings/appkit/combo_box.rb +22 -0
  54. data/lib/hotcocoa/mappings/appkit/control.rb +31 -0
  55. data/lib/hotcocoa/mappings/appkit/font.rb +58 -0
  56. data/lib/hotcocoa/mappings/appkit/gradient.rb +19 -0
  57. data/lib/hotcocoa/mappings/appkit/image.rb +15 -0
  58. data/lib/hotcocoa/mappings/appkit/image_view.rb +41 -0
  59. data/lib/hotcocoa/mappings/appkit/label.rb +23 -0
  60. data/lib/hotcocoa/mappings/appkit/layout_view.rb +9 -0
  61. data/lib/hotcocoa/mappings/appkit/line.rb +16 -0
  62. data/lib/hotcocoa/mappings/appkit/menu.rb +78 -0
  63. data/lib/hotcocoa/mappings/appkit/menu_item.rb +47 -0
  64. data/lib/hotcocoa/mappings/appkit/outline_view.rb +62 -0
  65. data/lib/hotcocoa/mappings/appkit/popup.rb +106 -0
  66. data/lib/hotcocoa/mappings/appkit/progress_indicator.rb +61 -0
  67. data/lib/hotcocoa/mappings/appkit/scroll_view.rb +28 -0
  68. data/lib/hotcocoa/mappings/appkit/search_field.rb +9 -0
  69. data/lib/hotcocoa/mappings/appkit/secure_text_field.rb +15 -0
  70. data/lib/hotcocoa/mappings/appkit/segmented_control.rb +103 -0
  71. data/lib/hotcocoa/mappings/appkit/slider.rb +23 -0
  72. data/lib/hotcocoa/mappings/appkit/sound.rb +9 -0
  73. data/lib/hotcocoa/mappings/appkit/speech_synthesizer.rb +22 -0
  74. data/lib/hotcocoa/mappings/appkit/split_view.rb +19 -0
  75. data/lib/hotcocoa/mappings/appkit/status_bar.rb +7 -0
  76. data/lib/hotcocoa/mappings/appkit/status_item.rb +9 -0
  77. data/lib/hotcocoa/mappings/appkit/table_view.rb +108 -0
  78. data/lib/hotcocoa/mappings/appkit/text_field.rb +36 -0
  79. data/lib/hotcocoa/mappings/appkit/text_view.rb +13 -0
  80. data/lib/hotcocoa/mappings/appkit/toolbar.rb +99 -0
  81. data/lib/hotcocoa/mappings/appkit/toolbar_item.rb +36 -0
  82. data/lib/hotcocoa/mappings/appkit/tracking_area.rb +24 -0
  83. data/lib/hotcocoa/mappings/appkit/view.rb +65 -0
  84. data/lib/hotcocoa/mappings/appkit/window.rb +124 -0
  85. data/lib/hotcocoa/mappings/foundation/net_service.rb +65 -0
  86. data/lib/hotcocoa/mappings/foundation/net_service_browser.rb +27 -0
  87. data/lib/hotcocoa/mappings/foundation/notification.rb +24 -0
  88. data/lib/hotcocoa/mappings/foundation/sort_descriptor.rb +17 -0
  89. data/lib/hotcocoa/mappings/foundation/timer.rb +38 -0
  90. data/lib/hotcocoa/mappings/foundation/user_defaults.rb +43 -0
  91. data/lib/hotcocoa/mappings/foundation/xml_parser.rb +44 -0
  92. data/lib/hotcocoa/mappings/qtkit/movie.rb +15 -0
  93. data/lib/hotcocoa/mappings/qtkit/movie_view.rb +28 -0
  94. data/lib/hotcocoa/mappings/webkit/web_view.rb +20 -0
  95. data/lib/hotcocoa/mappings.rb +57 -111
  96. data/lib/hotcocoa/mvc.rb +90 -81
  97. data/lib/hotcocoa/notification_listener.rb +135 -58
  98. data/lib/hotcocoa/standard_rake_tasks.rb +17 -9
  99. data/lib/hotcocoa/target_action_convenience.rb +39 -0
  100. data/lib/hotcocoa/template.rb +90 -21
  101. data/lib/hotcocoa/version.rb +3 -0
  102. data/lib/hotcocoa.rb +12 -14
  103. data/template/Rakefile +31 -3
  104. data/template/__APPLICATION_NAME__.appspec +8 -0
  105. data/template/lib/application.rb +14 -17
  106. data/template/lib/menu.rb +11 -11
  107. data/test/application/test_builder.rb +28 -0
  108. data/test/application/test_specification.rb +280 -0
  109. data/test/core_extensions/test_kernel.rb +66 -0
  110. data/test/core_extensions/test_nsarray.rb +9 -0
  111. data/test/core_extensions/test_nsmutable_attributed_string.rb +86 -0
  112. data/test/core_extensions/test_nsstring.rb +20 -0
  113. data/test/core_extensions/test_nsurl.rb +9 -0
  114. data/test/core_extensions/test_numeric.rb +10 -0
  115. data/test/core_extensions/test_object.rb +37 -0
  116. data/test/core_extensions/test_range.rb +28 -0
  117. data/test/helper.rb +30 -0
  118. data/test/mappings/test_bonjour.rb +79 -0
  119. data/test/mappings/test_color.rb +16 -0
  120. data/test/mappings/test_font.rb +21 -0
  121. data/test/mappings/test_tracking_area.rb +18 -0
  122. data/test/test_application_builder.rb +95 -0
  123. data/test/test_attributed_string_helpers.rb +63 -0
  124. data/test/test_behaviours.rb +26 -0
  125. data/test/test_bin.rb +75 -0
  126. data/test/test_mapper.rb +100 -0
  127. data/test/test_mapping_methods.rb +67 -0
  128. data/test/test_mappings.rb +68 -0
  129. data/test/test_notification_listener.rb +19 -0
  130. data/test/test_template.rb +65 -0
  131. metadata +217 -90
  132. data/lib/hotcocoa/attributed_string.rb +0 -143
  133. data/lib/hotcocoa/kernel_ext.rb +0 -14
  134. data/lib/hotcocoa/mappings/alert.rb +0 -25
  135. data/lib/hotcocoa/mappings/application.rb +0 -112
  136. data/lib/hotcocoa/mappings/box.rb +0 -39
  137. data/lib/hotcocoa/mappings/button.rb +0 -92
  138. data/lib/hotcocoa/mappings/collection_view.rb +0 -44
  139. data/lib/hotcocoa/mappings/color.rb +0 -28
  140. data/lib/hotcocoa/mappings/column.rb +0 -21
  141. data/lib/hotcocoa/mappings/combo_box.rb +0 -24
  142. data/lib/hotcocoa/mappings/control.rb +0 -33
  143. data/lib/hotcocoa/mappings/font.rb +0 -44
  144. data/lib/hotcocoa/mappings/gradient.rb +0 -15
  145. data/lib/hotcocoa/mappings/image.rb +0 -15
  146. data/lib/hotcocoa/mappings/image_view.rb +0 -43
  147. data/lib/hotcocoa/mappings/label.rb +0 -25
  148. data/lib/hotcocoa/mappings/layout_view.rb +0 -9
  149. data/lib/hotcocoa/mappings/menu.rb +0 -71
  150. data/lib/hotcocoa/mappings/menu_item.rb +0 -47
  151. data/lib/hotcocoa/mappings/movie.rb +0 -13
  152. data/lib/hotcocoa/mappings/movie_view.rb +0 -27
  153. data/lib/hotcocoa/mappings/notification.rb +0 -17
  154. data/lib/hotcocoa/mappings/popup.rb +0 -110
  155. data/lib/hotcocoa/mappings/progress_indicator.rb +0 -68
  156. data/lib/hotcocoa/mappings/scroll_view.rb +0 -29
  157. data/lib/hotcocoa/mappings/search_field.rb +0 -9
  158. data/lib/hotcocoa/mappings/secure_text_field.rb +0 -17
  159. data/lib/hotcocoa/mappings/segmented_control.rb +0 -97
  160. data/lib/hotcocoa/mappings/slider.rb +0 -25
  161. data/lib/hotcocoa/mappings/sort_descriptor.rb +0 -13
  162. data/lib/hotcocoa/mappings/sound.rb +0 -9
  163. data/lib/hotcocoa/mappings/speech_synthesizer.rb +0 -25
  164. data/lib/hotcocoa/mappings/split_view.rb +0 -21
  165. data/lib/hotcocoa/mappings/status_bar.rb +0 -7
  166. data/lib/hotcocoa/mappings/status_item.rb +0 -9
  167. data/lib/hotcocoa/mappings/table_view.rb +0 -110
  168. data/lib/hotcocoa/mappings/text_field.rb +0 -41
  169. data/lib/hotcocoa/mappings/text_view.rb +0 -13
  170. data/lib/hotcocoa/mappings/timer.rb +0 -25
  171. data/lib/hotcocoa/mappings/toolbar.rb +0 -100
  172. data/lib/hotcocoa/mappings/toolbar_item.rb +0 -36
  173. data/lib/hotcocoa/mappings/view.rb +0 -67
  174. data/lib/hotcocoa/mappings/web_view.rb +0 -22
  175. data/lib/hotcocoa/mappings/window.rb +0 -118
  176. data/lib/hotcocoa/mappings/xml_parser.rb +0 -41
  177. data/lib/hotcocoa/object_ext.rb +0 -22
  178. data/lib/hotcocoa/plist.rb +0 -45
  179. data/template/config/build.yml +0 -8
  180. data/test/test_helper.rb +0 -3
  181. data/test/test_hotcocoa.rb +0 -11
@@ -1,227 +1,366 @@
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
1
+ ##
2
+ # Does most of the heavy lifiting when it comes to HotCocoa mappings.
3
+ class HotCocoa::Mappings::Mapper
4
+
5
+ class << self
6
+ ##
7
+ # Add mappings to a class so instances of the class can benefit from
8
+ # HotCocoa features. Usually called by {HotCocoa::Behaviors.included}.
9
+ #
10
+ # @param [Class] klass
11
+ # @return [nil] do not count on a return value from this method
12
+ def map_class klass
13
+ new(klass).include_in_class
14
+ end
15
+
16
+ ##
17
+ # Create a mapper for the given `klass` and assign it to the
18
+ # given `builder_method`.
19
+ #
20
+ # @param [Class] klass
21
+ # @param [Symbol] builder_method
22
+ # @return [HotCocoa::Mappings::Mapper]
23
+ def map_instances_of klass, builder_method, &block
24
+ new(klass).map_method(builder_method, &block)
25
+ end
26
+
27
+ ##
28
+ # Cached bindings modules.
29
+ #
30
+ # @return [Hash{Symbol=>Module}]
31
+ attr_reader :bindings_modules
32
+
33
+ ##
34
+ # Cached delegate modules.
35
+ #
36
+ # @return [Hash{Symbol=>Module}]
37
+ attr_reader :delegate_modules
38
+ end
39
+
40
+ ##
41
+ # Performance hack. Put mutable objects that are constant into a
42
+ # constant to avoid having to `#dup`.
43
+ #
44
+ # @return [String]
45
+ SET = 'set'
46
+
47
+ # they need to be initialized
48
+ @bindings_modules = {}
49
+ @delegate_modules = {}
50
+
51
+ # @return [Class]
52
+ attr_reader :control_class
53
+
54
+ ##
55
+ # @todo We do not use the cached `builder_method` attribute unless
56
+ # you count tests. So maybe we should get rid of it?
57
+ #
58
+ # The name which the mapping goes by (e.g. :window for NSWindow)
59
+ #
60
+ # @return [Symbol]
61
+ attr_reader :builder_method
62
+
63
+ ##
64
+ # Singleton class for the mapper instance
65
+ #
66
+ # @return [Class]
67
+ attr_reader :control_module
68
+
69
+ ##
70
+ # Whether or not bindings should be mapped for an instance of
71
+ # the mapped class.
72
+ #
73
+ # @return [Boolean]
74
+ attr_accessor :map_bindings
75
+
76
+ # @param [Class] klass the class that is being mapped
77
+ def initialize klass
78
+ @control_class = klass
79
+ end
80
+
81
+ ##
82
+ # Add HotCocoa features to a class. The `control_class` that the mapper
83
+ # was initialized with will receive features for all ancestors that
84
+ # have mappings.
85
+ def include_in_class
86
+ @extension_method = :include
87
+ customize @control_class
88
+ end
89
+
90
+ ##
91
+ # Create the mapping method named `builder_method`.
92
+ #
93
+ # @param [Symbol] builder_method
94
+ # @yield
95
+ # @return [HotCocoa::Mappings::Mapper]
96
+ def map_method builder_method, &block
97
+ @extension_method = :extend
98
+ @builder_method = builder_method
99
+
100
+ # @todo use self.singleton_class instead (not implemented in MacRuby yet)
101
+ mod = (class << self; self; end)
102
+ mod.extend HotCocoa::MappingMethods
103
+ mod.module_eval &block
104
+
105
+ @control_module = mod
106
+ # put self in a variable, because context of self changes inside the define_method block
107
+ inst = self
108
+ HotCocoa.send :define_method, builder_method do |args = {}, &control_block|
109
+ map = inst.remap_constants args
110
+
111
+ inst.map_bindings = map.delete :map_bindings
112
+ default_empty_rect_used = (CGRectZero == map[:frame])
113
+
114
+ control = if inst.respond_to? :init_with_options
115
+ inst.init_with_options(inst.control_class.alloc, map)
116
+ else
117
+ inst.alloc_with_options(map)
118
+ end
119
+
120
+ inst.customize control
121
+
122
+ map.each do |key, value|
123
+ if control.respond_to? "#{key}="
124
+ control.send "#{key}=", value
125
+
126
+ elsif control.respond_to? key
127
+ new_key = (key.start_with?(SET) ? key : "set#{key[0].capitalize}#{key[1..-1]}")
128
+ if control.respond_to? new_key
129
+ control.send new_key, value
130
+
131
+ else
132
+ control.send key
133
+
76
134
  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)
135
+ elsif control.respond_to? "set#{key.camel_case}"
136
+ control.send "set#{key.camel_case}", value
137
+
138
+ else
139
+ NSLog("Unable to map #{key} as a method")
140
+
90
141
  end
91
- constants
92
142
  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
143
+
144
+ if default_empty_rect_used
145
+ control.sizeToFit if control.respondsToSelector :sizeToFit
100
146
  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
147
+
148
+ if control_block
149
+ if inst.respond_to? :handle_block
150
+ inst.handle_block control, &control_block
151
+ else
152
+ control_block.call control
106
153
  end
107
- methods
108
154
  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
155
+
156
+ control
157
+ end
158
+
159
+ # make the function callable using HotCocoa.xxxx
160
+ HotCocoa.send :module_function, builder_method
161
+ # module_function makes the instance method private, but we want it to stay public
162
+ HotCocoa.send :public, builder_method
163
+ self
164
+ end
165
+
166
+ ##
167
+ # Returns a hash of constant hashes that were inherited from ancestors
168
+ # that have also been mapped.
169
+ #
170
+ # @return [Hash{Hash}]
171
+ def inherited_constants
172
+ constants = {}
173
+ each_control_ancestor do |ancestor|
174
+ constants.merge! ancestor.control_module.constants_map
175
+ end
176
+ constants
177
+ end
178
+
179
+ def inherited_delegate_methods
180
+ delegate_methods = {}
181
+ each_control_ancestor do |ancestor|
182
+ delegate_methods.merge! ancestor.control_module.delegate_map
183
+ end
184
+ delegate_methods
185
+ end
186
+
187
+ ##
188
+ # Return the `custom_methods` module for the class we are instantiating,
189
+ # as well as all of its ancestors.
190
+ #
191
+ # @return [Array<Module>]
192
+ def inherited_custom_methods
193
+ methods = []
194
+ each_control_ancestor do |ancestor|
195
+ if ancestor.control_module.custom_methods
196
+ methods << ancestor.control_module.custom_methods
116
197
  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)
198
+ end
199
+ methods
200
+ end
201
+
202
+ ##
203
+ # Iterates over the ancestor chain for the class being mapped and
204
+ # yields for each ancestor that also has a mapping.
205
+ #
206
+ # Classes are yielded from the descending order (from the super class
207
+ # to the sub class).
208
+ #
209
+ # @yield
210
+ # @yieldparam [Class] a class in the inheritance chain
211
+ def each_control_ancestor
212
+ control_class.ancestors.reverse.each do |ancestor|
213
+ HotCocoa::Mappings.mappings.values.each do |mapper|
214
+ yield mapper if mapper.control_class == ancestor
124
215
  end
125
-
126
- def decorate_with_delegate_methods(control)
127
- control.send(@extension_method, delegate_module_for_control_class)
216
+ end
217
+ end
218
+
219
+ ##
220
+ # Apply customizations to defined in a mapping to the control. The
221
+ # control is either an instance of the class or the class itself,
222
+ # depending on how things were setup.
223
+ #
224
+ # @param control
225
+ def customize control
226
+ inherited_custom_methods.each do |custom_methods|
227
+ control.send @extension_method, custom_methods
228
+ end
229
+ decorate_with_delegate_methods control
230
+ decorate_with_bindings_methods control
231
+ end
232
+
233
+ ##
234
+ # Add the delegate method hooks. For #include they become instance methods
235
+ # and for #extend they become singleton methods.
236
+ def decorate_with_delegate_methods control
237
+ control.send @extension_method, delegate_module_for_control_class
238
+ end
239
+
240
+ ##
241
+ # Create a module to hold the delegate object. The module can then be
242
+ # mixed in so that a control instance can use HotCocoa style delegation.
243
+ #
244
+ # The style of delegation that HotCocoa supports works by creating an
245
+ # Object instance and then defining delegate methods as singleton
246
+ # methods on that object. Then the object is set to be the delegate
247
+ # of the control.
248
+ #
249
+ # The generated module is cached for later reuse.
250
+ #
251
+ # @return [Module] the generated delegate module
252
+ def delegate_module_for_control_class
253
+ delegate_module = HotCocoa::Mappings::Mapper.delegate_modules[control_class]
254
+ return delegate_module if delegate_module
255
+
256
+ delegate_module = Module.new
257
+ required_methods = []
258
+ delegate_methods = inherited_delegate_methods
259
+
260
+ if delegate_methods.size > 0
261
+ delegate_methods.each do |delegate_method, mapping|
262
+ required_methods << delegate_method if mapping[:required]
128
263
  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
264
+
265
+ delegate_methods.each do |delegate_method, mapping|
266
+ parameters = mapping[:parameters] ? mapping[:parameters] : []
267
+
268
+ # kind of a hack, giving a block directly to define_method is not working
269
+ # for some odd reason, possibly a bug in MacRuby
270
+ callback = Proc.new do |&block|
271
+ raise 'Must pass in a block to use this delegate method' unless block
272
+
273
+ @_delegate_builder ||= HotCocoa::DelegateBuilder.new(self, required_methods)
274
+ @_delegate_builder.add_delegated_method(block, delegate_method, *parameters)
157
275
  end
158
- Mapper.delegate_modules[control_class]
276
+ delegate_module.send :define_method, mapping[:to], callback
159
277
  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
278
+
279
+ delegate_module.send :define_method, :delegate_to do |object|
280
+ @_delegate_builder ||= HotCocoa::DelegateBuilder.new(self, required_methods)
281
+ @_delegate_builder.delegate_to(object, *delegate_methods.values.map { |method| method[:to].to_sym })
164
282
  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
283
+ end
284
+
285
+ HotCocoa::Mappings::Mapper.delegate_modules[control_class] = delegate_module
286
+ end
287
+
288
+ # @return [nil] do not count on a return value
289
+ def decorate_with_bindings_methods control
290
+ return if control_class == NSApplication
291
+ if @map_bindings
292
+ control.send @extension_method, bindings_module_for_control(control)
293
+ end
294
+ end
295
+
296
+ ##
297
+ # Create a module to hold all bindings setters. The bindings module is
298
+ # meant to assist with setting up Cocoa Bindings by providing a simplified
299
+ # and more Ruby-ish interface.
300
+ #
301
+ # Read more about [Key-Value Binding](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Protocols/NSKeyValueBindingCreation_Protocol/Reference/Reference.html).
302
+ #
303
+ # If the control has no exposed bindings, then an empty module will
304
+ # be generated.
305
+ #
306
+ # In either case, once a module is generated, it is cached for later use.
307
+ #
308
+ # @return [Module] the generated bindings module
309
+ def bindings_module_for_control control
310
+ bindings_module = HotCocoa::Mappings::Mapper.bindings_modules[control_class]
311
+ return bindings_module if bindings_module
312
+
313
+ instance = if control == control_class
314
+ control_class.alloc.init
315
+ else
316
+ control
317
+ end
318
+
319
+ bindings_module = Module.new
320
+ instance.exposedBindings.each do |exposed_binding|
321
+ p = Proc.new do |value|
322
+ if value.kind_of? Hash
323
+ options = value.delete :options
324
+ bind "#{exposed_binding}", toObject: value.keys.first,
325
+ withKeyPath: value.values.first,
326
+ options: options
170
327
  else
171
- control
328
+ send "set#{exposed_binding.camel_case}", value
172
329
  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
330
  end
331
+ bindings_module.send :define_method, "#{exposed_binding.underscore}=", p
332
+ end
189
333
 
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
334
+ HotCocoa::Mappings::Mapper.bindings_modules[control_class] = bindings_module
335
+ end
336
+
337
+ ##
338
+ # Takes a hash and processes symbols, if the symbol is a mapped
339
+ # constant then it will be swapped with the value of the constant.
340
+ #
341
+ # This is how constant mappings are used in Hot Cocoa.
342
+ #
343
+ # @param [Hash] tags
344
+ # @return [Hash]
345
+ def remap_constants tags
346
+ constants = inherited_constants
347
+ if control_module.defaults
348
+ control_module.defaults.each do |key, value|
349
+ tags[key] = value unless tags.has_key? key
222
350
  end
351
+ end
223
352
 
224
-
353
+ result = {}
354
+ tags.each do |tag, value|
355
+ if constants[tag]
356
+ result[tag] = value.kind_of?(Array) ?
357
+ value.inject(0) { |a, i| a|constants[tag][i] } :
358
+ constants[tag][value]
359
+ else
360
+ result[tag] = value
361
+ end
225
362
  end
363
+ result
226
364
  end
227
- end
365
+
366
+ end