motion-kit 0.0.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +839 -0
  3. data/lib/motion-kit-cocoa/cocoa_util.rb +59 -0
  4. data/lib/motion-kit-cocoa/constraints/constraint.rb +765 -0
  5. data/lib/motion-kit-cocoa/constraints/constraint_placeholder.rb +22 -0
  6. data/lib/motion-kit-cocoa/constraints/constraints_layout.rb +536 -0
  7. data/lib/motion-kit-cocoa/constraints/constraints_target.rb +52 -0
  8. data/lib/motion-kit-cocoa/layouts/cagradientlayer_layout.rb +13 -0
  9. data/lib/motion-kit-cocoa/layouts/calayer_layout.rb +27 -0
  10. data/lib/motion-kit-cocoa/layouts/sugarcube_compat.rb +38 -0
  11. data/lib/motion-kit-ios/dummy.rb +93 -0
  12. data/lib/motion-kit-ios/ios_util.rb +20 -0
  13. data/lib/motion-kit-ios/layouts/constraints_layout.rb +22 -0
  14. data/lib/motion-kit-ios/layouts/layout_device.rb +32 -0
  15. data/lib/motion-kit-ios/layouts/layout_orientation.rb +47 -0
  16. data/lib/motion-kit-ios/layouts/uibutton_layout.rb +52 -0
  17. data/lib/motion-kit-ios/layouts/uiview_layout.rb +45 -0
  18. data/lib/motion-kit-ios/layouts/uiview_layout_autoresizing.rb +75 -0
  19. data/lib/motion-kit-ios/layouts/uiview_layout_constraints.rb +36 -0
  20. data/lib/motion-kit-ios/layouts/uiview_layout_frame.rb +307 -0
  21. data/lib/motion-kit-ios/layouts/uiview_layout_gradient.rb +20 -0
  22. data/lib/motion-kit-osx/dummy.rb +89 -0
  23. data/lib/motion-kit-osx/layouts/constraints_layout.rb +42 -0
  24. data/lib/motion-kit-osx/layouts/nsmenu_extensions.rb +186 -0
  25. data/lib/motion-kit-osx/layouts/nsmenu_layout.rb +109 -0
  26. data/lib/motion-kit-osx/layouts/nsmenuitem_extensions.rb +45 -0
  27. data/lib/motion-kit-osx/layouts/nstablecolumn_layout.rb +12 -0
  28. data/lib/motion-kit-osx/layouts/nstableview_layout.rb +20 -0
  29. data/lib/motion-kit-osx/layouts/nsview_layout.rb +47 -0
  30. data/lib/motion-kit-osx/layouts/nsview_layout_autoresizing.rb +75 -0
  31. data/lib/motion-kit-osx/layouts/nsview_layout_constraints.rb +34 -0
  32. data/lib/motion-kit-osx/layouts/nsview_layout_frame.rb +375 -0
  33. data/lib/motion-kit-osx/layouts/nswindow_frame.rb +14 -0
  34. data/lib/motion-kit-osx/layouts/nswindow_layout.rb +77 -0
  35. data/lib/motion-kit-osx/osx_util.rb +16 -0
  36. data/lib/motion-kit.rb +33 -0
  37. data/lib/motion-kit/calculate.rb +263 -0
  38. data/lib/motion-kit/layouts/base_layout.rb +299 -0
  39. data/lib/motion-kit/layouts/base_layout_class_methods.rb +43 -0
  40. data/lib/motion-kit/layouts/parent.rb +68 -0
  41. data/lib/motion-kit/layouts/view_layout.rb +327 -0
  42. data/lib/motion-kit/motion-kit.rb +18 -0
  43. data/lib/motion-kit/object.rb +16 -0
  44. data/lib/motion-kit/util.rb +20 -0
  45. data/lib/motion-kit/version.rb +1 -1
  46. data/spec/ios/apply_styles_spec.rb +21 -0
  47. data/spec/ios/autoresizing_helper_spec.rb +224 -0
  48. data/spec/ios/calculate_spec.rb +322 -0
  49. data/spec/ios/calculator_spec.rb +31 -0
  50. data/spec/ios/constraints_helpers/attribute_lookup_spec.rb +27 -0
  51. data/spec/ios/constraints_helpers/axis_lookup_spec.rb +13 -0
  52. data/spec/ios/constraints_helpers/center_constraints_spec.rb +419 -0
  53. data/spec/ios/constraints_helpers/constraint_placeholder_spec.rb +72 -0
  54. data/spec/ios/constraints_helpers/priority_lookup_spec.rb +19 -0
  55. data/spec/ios/constraints_helpers/relationship_lookup_spec.rb +27 -0
  56. data/spec/ios/constraints_helpers/relative_corners_spec.rb +274 -0
  57. data/spec/ios/constraints_helpers/relative_location_spec.rb +111 -0
  58. data/spec/ios/constraints_helpers/simple_constraints_spec.rb +2763 -0
  59. data/spec/ios/constraints_helpers/size_constraints_spec.rb +422 -0
  60. data/spec/ios/constraints_helpers/view_lookup_constraints_spec.rb +93 -0
  61. data/spec/ios/create_layout_spec.rb +40 -0
  62. data/spec/ios/custom_layout_spec.rb +13 -0
  63. data/spec/ios/deferred_spec.rb +89 -0
  64. data/spec/ios/device_helpers_spec.rb +51 -0
  65. data/spec/ios/frame_helper_spec.rb +1150 -0
  66. data/spec/ios/layer_layout_spec.rb +36 -0
  67. data/spec/ios/layout_extensions_spec.rb +70 -0
  68. data/spec/ios/layout_spec.rb +74 -0
  69. data/spec/ios/layout_state_spec.rb +27 -0
  70. data/spec/ios/motionkit_util_spec.rb +102 -0
  71. data/spec/ios/objc_selectors_spec.rb +10 -0
  72. data/spec/ios/orientation_helper_specs.rb +67 -0
  73. data/spec/ios/parent_layout_spec.rb +19 -0
  74. data/spec/ios/parent_spec.rb +45 -0
  75. data/spec/ios/remove_layout_spec.rb +25 -0
  76. data/spec/ios/root_layout_spec.rb +53 -0
  77. data/spec/ios/setters_spec.rb +63 -0
  78. data/spec/ios/uibutton_layout_spec.rb +24 -0
  79. data/spec/ios/uitextfield_spec.rb +14 -0
  80. data/spec/ios/view_attr_spec.rb +25 -0
  81. data/spec/osx/autoresizing_helper_spec.rb +224 -0
  82. data/spec/osx/constraints_helper_spec.rb +0 -0
  83. data/spec/osx/constraints_helpers/orientation_lookup_spec.rb +13 -0
  84. data/spec/osx/constraints_helpers/simple_constraints_spec.rb +2095 -0
  85. data/spec/osx/constraints_helpers/size_constraints_spec.rb +362 -0
  86. data/spec/osx/create_menu_spec.rb +14 -0
  87. data/spec/osx/create_via_extensions_spec.rb +63 -0
  88. data/spec/osx/deferred_spec.rb +85 -0
  89. data/spec/osx/frame_helper_spec.rb +1881 -0
  90. data/spec/osx/menu_extensions_spec.rb +376 -0
  91. data/spec/osx/menu_layout_spec.rb +157 -0
  92. data/spec/osx/menu_spec.rb +70 -0
  93. data/spec/osx/root_menu_spec.rb +15 -0
  94. metadata +166 -14
@@ -0,0 +1,43 @@
1
+ # @provides MotionKit::BaseLayoutClassMethods
2
+ module MotionKit
3
+ module BaseLayoutClassMethods
4
+ def target_klasses
5
+ # We don't want subclasses, just BaseLayout
6
+ return BaseLayout.target_klasses unless self == BaseLayout
7
+ @target_klasses ||= {}
8
+ end
9
+
10
+ def targets(klass=nil)
11
+ return nil if klass.nil? && self == BaseLayout
12
+ return @targets || superclass.targets if klass.nil?
13
+ @targets = klass
14
+ BaseLayout.target_klasses[klass] = self
15
+ nil
16
+ end
17
+
18
+ # Instantiates a new Layout instance using `layout` as the root-level layout
19
+ def layout_for(layout, klass)
20
+ memoized_klass = memoize(klass)
21
+ memoized_klass.new_child(layout) if memoized_klass
22
+ end
23
+
24
+ # Cache registered classes
25
+ def memoize(klass)
26
+ @memoize ||= {}
27
+ @memoize[klass] ||= begin
28
+ while klass
29
+ break if registered_class = target_klasses[klass]
30
+ klass = klass.superclass
31
+ end
32
+ @memoize[klass] = registered_class if registered_class
33
+ end
34
+ @memoize[klass]
35
+ end
36
+
37
+ def new_child(layout=nil)
38
+ child = self.new
39
+ child.set_layout(layout)
40
+ child
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,68 @@
1
+ # @provides MotionKit::Parent
2
+ module MotionKit
3
+ # Simple class that returns data about the parent element
4
+ # for use while setting the styles of a child element.
5
+ class Parent
6
+
7
+ attr_reader :element
8
+
9
+ def initialize(element)
10
+ @element = element
11
+ end
12
+
13
+ def origin
14
+ try(:frame, :origin)
15
+ end
16
+
17
+ def size
18
+ try(:frame, :size)
19
+ end
20
+
21
+ def x
22
+ try(:frame, :origin, :x)
23
+ end
24
+
25
+ def y
26
+ try(:frame, :origin, :y)
27
+ end
28
+
29
+ def width
30
+ try(:frame, :size, :width)
31
+ end
32
+
33
+ def height
34
+ try(:frame, :size, :height)
35
+ end
36
+
37
+ def center_x
38
+ width / 2.0 if width
39
+ end
40
+
41
+ def center_y
42
+ height / 2.0 if height
43
+ end
44
+
45
+ def center
46
+ CGPointMake(center_x, center_y) if width && height
47
+ end
48
+
49
+ protected
50
+
51
+ # Convenience method that takes a list of method calls and tries
52
+ # them end-to-end, returning nil if any fail to respond to that
53
+ # method name.
54
+ # Very similar to ActiveSupport's `.try` method.
55
+ def try(*method_chain)
56
+ obj = self.element
57
+ method_chain.each do |m|
58
+ # We'll break out and return nil if any part of the chain
59
+ # doesn't respond properly.
60
+ (obj = nil) && break unless obj.respond_to?(m)
61
+ obj = obj.send(m)
62
+ end
63
+ obj
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,327 @@
1
+ # @provides MotionKit::ViewLayout
2
+ # @requires MotionKit::BaseLayout
3
+ module MotionKit
4
+ # A sensible parent class for any View-like layout class. Platform agnostic.
5
+ # Any platform-specific tasks are offloaded to child views (add_child,
6
+ # remove_child).
7
+ # Actually, "view like" is misleading, since technically it only assumes "tree
8
+ # like". You could use a ViewLayout subclass to construct a hierarchy
9
+ # representing a family tree, for instance. But that would be a silly use of
10
+ # MotionKit.
11
+ class ViewLayout < BaseLayout
12
+
13
+ class << self
14
+
15
+ # This is an `attr_reader`-like method that also calls `build_view` if the
16
+ # @view doesn't exist, and so you can use it to refer to views that are
17
+ # assigned to ivars in your `layout` method.
18
+ #
19
+ # @example
20
+ # class MyLayout < MK::Layout
21
+ # view :label
22
+ # view :login_button
23
+ #
24
+ # def layout
25
+ # # if element id and attr name match, no need to assign to ivar
26
+ # add UILabel, :label
27
+ # # if they don't match you must assign. If you are using
28
+ # # Key-Value observation you should use the setter:
29
+ # self.login_button = add UIButton, :button
30
+ # end
31
+ #
32
+ # end
33
+ def view(name)
34
+ ivar_name = "@#{name}"
35
+ define_method(name) do
36
+ unless instance_variable_get(ivar_name)
37
+ view = self.get(name)
38
+ unless view
39
+ build_view unless @view
40
+ view = instance_variable_get(ivar_name) || self.get(name)
41
+ end
42
+ self.send("#{name}=", view)
43
+ return view
44
+ end
45
+ return instance_variable_get(ivar_name)
46
+ end
47
+ # KVO compliance
48
+ attr_writer name
49
+ end
50
+
51
+ end
52
+
53
+ # The parent view. This method builds the layout and returns the root view.
54
+ def view
55
+ if @layout != self
56
+ return @layout.view
57
+ end
58
+ @view ||= build_view
59
+ end
60
+
61
+ # Assign a view to act as the 'root' view for this layout. This method can
62
+ # only be called once, and must be called before `add` is called for the
63
+ # first time (otherwise `add` will create a default root view). This method
64
+ # must be called from inside `layout`, otherwise you should just use
65
+ # `create`.
66
+ #
67
+ # You can also call this method with just an element_id, and the default
68
+ # root view will be created.
69
+ def root(element, element_id=nil, &block)
70
+ if @view
71
+ raise ContextConflictError.new("Already created the root view")
72
+ end
73
+ unless @assign_root
74
+ raise InvalidRootError.new("You should only create a 'root' view from inside the 'layout' method (use 'create' elsewhere)")
75
+ end
76
+ @assign_root = false
77
+
78
+ # this method can be called with just a symbol, to assign the root element_id
79
+ if element.is_a?(Symbol)
80
+ element_id = element
81
+ # See note below about why we don't need to `apply(:default_root)`
82
+ element = default_root
83
+ end
84
+
85
+ @view = initialize_view(element)
86
+ if block
87
+ if @context
88
+ raise ContextConflictError.new("Already in a context")
89
+ end
90
+
91
+ context(@view) do
92
+ # We're just using the `create` method for its side effects: calling the
93
+ # style method and calling the block.
94
+ create(@view, element_id, &block)
95
+ end
96
+ elsif element_id
97
+ create(@view, element_id)
98
+ end
99
+
100
+ return @view
101
+ end
102
+
103
+ # instantiates a view, possibly running a 'layout block' to add child views.
104
+ def create(element, element_id=nil, &block)
105
+ element = initialize_view(element)
106
+
107
+ if element_id
108
+ # We set the optional id and call the '_style' method, if it's been
109
+ # defined.
110
+ element.motion_kit_id = element_id
111
+ self.call_style_method(element, element_id)
112
+ end
113
+
114
+ if block
115
+ context(element, &block)
116
+ end
117
+
118
+ element
119
+ end
120
+
121
+ def call_style_method(element, element_id)
122
+ style_method = "#{element_id}_style"
123
+ if @layout.respond_to?(style_method)
124
+ @layout.context(element) do
125
+ @layout.send(style_method)
126
+ end
127
+ end
128
+ return element
129
+ end
130
+
131
+ # Calls the style method of all objects in the view hierarchy
132
+ def reapply!(root=nil)
133
+ root ||= self.view
134
+ @layout_state = :reapply
135
+ MotionKit.find_all_views(root) do |view|
136
+ call_style_method(view, view.motion_kit_id) if view.motion_kit_id
137
+ end
138
+ @layout_state = :initial
139
+
140
+ return self
141
+ end
142
+
143
+ # Calls the style method of all objects in the view hierarchy
144
+ def reapply(&block)
145
+ raise ArgumentError.new('Block required') unless block
146
+
147
+ if @layout_state == :reapply
148
+ yield
149
+ end
150
+ return self
151
+ end
152
+
153
+ def initial(&block)
154
+ raise ArgumentError.new('Block required') unless block
155
+
156
+ if @layout_state == :initial
157
+ yield
158
+ end
159
+ return self
160
+ end
161
+
162
+ # Delegates to `create` to instantiate a view and run a layout block, and
163
+ # adds the view to the current view on the view stack. If no view exists on
164
+ # the stack, a default root view can be created if that has been enabled.
165
+ def add(element, element_id=nil, &block)
166
+ # make sure we have a target - raises NoContextError if none exists
167
+ self.target
168
+
169
+ element = initialize_view(element)
170
+ unless @context
171
+ create_default_root_context
172
+ end
173
+ self.apply(:add_child, element)
174
+ create(element, element_id, &block)
175
+
176
+ element
177
+ end
178
+
179
+ # Retrieves a view by its element id. This will return the *first* view
180
+ # with this element_id in the tree, where *first* means the view closest to
181
+ # the root view. Aliased to `first` to distinguish it from `last`.
182
+ def get(element_id)
183
+ if @layout != self
184
+ return @layout.get(element_id)
185
+ end
186
+ self.get(element_id, in: self.view)
187
+ end
188
+ def first(element_id) ; get(element_id) ; end
189
+
190
+ # Same as `get`, but with the root view specified.
191
+ def get(element_id, in: root)
192
+ MotionKit.find_first_view(root) { |view| view.motion_kit_id == element_id }
193
+ end
194
+ def first(element_id, in: root) ; get(element_id, in: root) ; end
195
+
196
+ # Retrieves a view by its element id. This will return the *last* view
197
+ # with this element_id, where last means the view deepest and furthest from
198
+ # the root view.
199
+ def last(element_id)
200
+ if @layout != self
201
+ return @layout.last(element_id)
202
+ end
203
+ self.last(element_id, in: self.view)
204
+ end
205
+
206
+ # Same as `last`, but with the root view specified.
207
+ def last(element_id, in: root)
208
+ MotionKit.find_last_view(root) { |view| view.motion_kit_id == element_id }
209
+ end
210
+
211
+ # Returns all the elements with a given element_id in the view tree.
212
+ def all(element_id)
213
+ if @layout != self
214
+ return @layout.all(element_id)
215
+ end
216
+ self.all(element_id, in: self.view)
217
+ end
218
+
219
+ # Same as `all`, but with the root view specified.
220
+ def all(element_id, in: root)
221
+ MotionKit.find_all_views(root) { |view| view.motion_kit_id == element_id }
222
+ end
223
+
224
+ # Returns all the elements with a given element_id
225
+ def nth(element_id, index)
226
+ if @layout != self
227
+ return @layout.nth(element_id, index)
228
+ end
229
+ self.all(element_id, in: self.view)[index]
230
+ end
231
+
232
+ # Same as `nth`, but with the root view specified.
233
+ def nth(element_id, at: index, in: root)
234
+ self.all(element_id, in: root)[index]
235
+ end
236
+
237
+ # Removes a view (or several with the same name) from the hierarchy
238
+ # and forgets it entirely. Returns the views that were removed.
239
+ def remove(element_id)
240
+ if @layout != self
241
+ return @layout.remove(element_id)
242
+ end
243
+ self.remove(element_id, from: self.view)
244
+ end
245
+
246
+ # Same as `remove`, but with the root view specified.
247
+ def remove(element_id, from: root)
248
+ context(root) do
249
+ all(element_id).each do |subview|
250
+ self.apply(:remove_child, subview)
251
+ end
252
+ end
253
+ end
254
+
255
+ def create_default_root_context
256
+ if @assign_root
257
+ # Originally I thought default_root should be `apply`ied like other
258
+ # view-related methods, but actually this method *only* gets called
259
+ # from within the `layout` method, and so should already be inside the
260
+ # correct Layout subclass.
261
+ @context = root(default_root)
262
+ else
263
+ raise NoContextError.new("No top level view specified (missing outer 'create' method?)")
264
+ end
265
+ end
266
+
267
+ protected
268
+
269
+ # This method builds the layout and returns the root view.
270
+ def build_view
271
+ # Only in the 'layout' method will we allow default container to be
272
+ # created automatically (when 'add' is called)
273
+ @assign_root = true
274
+ was_top_level = @is_top_level
275
+ @is_top_level = true
276
+ layout
277
+ unless @view
278
+ if @assign_root
279
+ create_default_root_context
280
+ else
281
+ NSLog('Warning! No root view was set in ViewLayout#layout. Did you mean to call `root`?')
282
+ end
283
+ end
284
+ run_deferred(@view)
285
+ @is_top_level = was_top_level
286
+ @assign_root = false
287
+ # context can be set via the 'create_default_root_context' method, which
288
+ # may be outside a 'context' block, so make sure to restore context to
289
+ # it's previous value
290
+ @context = nil
291
+
292
+ @view
293
+ end
294
+
295
+ overrides :run_deferred
296
+ def run_deferred(top_level_context)
297
+ view_was = @view
298
+ @view = top_level_context
299
+ retval = super
300
+ @view = view_was
301
+
302
+ return retval
303
+ end
304
+
305
+ def layout
306
+ end
307
+
308
+ # Initializes an instance of a view. This will need to be smarter going
309
+ # forward as `new` isn't always the designated initializer.
310
+ #
311
+ # Accepts a view instance, a class (which is instantiated with 'new') or a
312
+ # `ViewLayout`, which returns the root view.
313
+ def initialize_view(elem)
314
+ if elem.is_a?(Class) && elem < ViewLayout
315
+ elem = elem.new_child(@layout, nil, self).view
316
+ elsif elem.is_a?(Class)
317
+ elem = elem.new
318
+ elsif elem.is_a?(ViewLayout)
319
+ elem = elem.view
320
+ end
321
+
322
+ return elem
323
+ end
324
+
325
+ end
326
+
327
+ end
@@ -0,0 +1,18 @@
1
+ # @provides MotionKit::Error
2
+ module MotionKit
3
+ class NoContextError < Exception
4
+ end
5
+ class ContextConflictError < Exception
6
+ end
7
+ class InvalidRootError < Exception
8
+ end
9
+ class InvalidDeferredError < Exception
10
+ end
11
+ class ApplyError < Exception
12
+ end
13
+ class NoSuperviewError < Exception
14
+ end
15
+ class NoCommonAncestorError < Exception
16
+ end
17
+ end
18
+ ::MK = MotionKit unless defined?(::MK)