ruby_motion_query 0.5.8 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,557 @@
1
+ module RubyMotionQuery
2
+
3
+ class RMQ
4
+ # @return RubyMotionQuery::Rect or array of RubyMotionQuery::Rect
5
+ #
6
+ # @example
7
+ # left = rmq(my_view).frame.left
8
+ def frame(params = nil)
9
+ if params
10
+ frame = params
11
+ self
12
+ else
13
+ if selected.length == 1
14
+ Rect.frame_for_view(selected.first)
15
+ else
16
+ selected.map{|s| Rect.frame_for_view(s)}
17
+ end
18
+ end
19
+ end
20
+
21
+ # Same as layout:
22
+ # rmq(my_view).layout(l: 10, t: 20, w: 100, h: 150)
23
+ #
24
+ # Always applied in this order, regardless of the hash order:
25
+ # grid
26
+ # l, t, w, h
27
+ # previous
28
+ # from_right, from_bottom
29
+ # right, bottom
30
+ # left and right applied together (will change width)
31
+ # top and bottom applied together (will change height)
32
+ # centered
33
+ # padding
34
+ #
35
+ # @example
36
+ # rmq(my_view).frame = :full
37
+ # rmq(my_view).frame = {l: 10, t: 20, w: 100, h: 150}
38
+ # rmq(my_view).frame = {t: 20, h: 150, l: 10, w: 100}
39
+ # rmq(my_view).frame = {l: 10, t: 20}
40
+ # rmq(my_view).frame = {h: 20}
41
+ # rmq(my_view).frame = {l: :prev, t: 20, w: 100, h: 150}
42
+ # rmq(my_view).frame = {l: 10, below_prev: 10, w: prev, h: 150}
43
+ # rmq(my_view).frame = {left: 10, top: 20, width: 100, height: 150}
44
+ # rmq(my_view).frame = {l: 10, t: 10, fr: 10, fb: 10}
45
+ # rmq(my_view).frame = {width: 50, height: 20, centered: :both}
46
+ # rmq(my_view).frame = "a1:b5"
47
+ # rmq(my_view, my_other_view).frame = {grid: "b2", w: 100, h: 200}
48
+ # rmq(my_view, my_other_view).frame = {g: "b2", w: 100, h: 200}
49
+ #
50
+ # @example with padding
51
+ # rmq(my_view).frame = {grid: "b2:d14", padding: 5}
52
+ # rmq(my_view).frame = {grid: "b2:d14", padding: {l: 5, t: 0, r: 5, b:0}}
53
+ def frame=(value)
54
+ selected.each do |view|
55
+ RubyMotionQuery::Rect.update_view_frame(view, value)
56
+ end
57
+ end
58
+
59
+ def bounds
60
+ if selected.length == 1
61
+ RubyMotionQuery::Rect.bounds_for_view(selected.first)
62
+ else
63
+ selected.map{|s| Rect.bounds_for_view(s)}
64
+ end
65
+ end
66
+
67
+ def bounds=(value)
68
+ selected.each do |view|
69
+ RubyMotionQuery::Rect.bounds_for_view(view).update(value, self.grid).apply_to_bounds
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+
76
+ # RMQ Rect
77
+ #
78
+ # *******************---*******---*************************** value options
79
+ # * | | * -------------
80
+ # * | | * Integer
81
+ # * | | * signed Integer
82
+ # * top | * Float
83
+ # * | | * String
84
+ # * | | *
85
+ # * --- | * also
86
+ # * ***************|***** --- * -----------------------
87
+ # * * view | * | * :full
88
+ # * * | * | * :right_of_prev (:rop)
89
+ # * * bottom * | * :left_of_prev (:lop)
90
+ # * * | * | * :below_prev (:bp)
91
+ # *|--- left ---|* | * | * :above_prev (:ap)
92
+ # * * | * height * :grid (:g)
93
+ # * * | * | * :padding (:p)
94
+ # * * | * | * int or hash: l,t,b,r
95
+ # *|-------------------- right -+---|* | *
96
+ # * * | * | * abbreviations
97
+ # * * | * |--+--from_right----|* -----------------------
98
+ # * * --- * | * :l, :t, :w, :h
99
+ # * ***************---*** --- * :r, :b
100
+ # * | * :fr, fb
101
+ # * |------ width - + -| *
102
+ # * | * :centered options
103
+ # * | * -----------------------
104
+ # * from_bottom * :horizontal
105
+ # * | * :vertical
106
+ # * | * :both
107
+ # * --- *
108
+ # ***********************************************************
109
+ #
110
+ class Rect
111
+ attr_reader :view
112
+
113
+ class << self
114
+
115
+ def update_view_frame(view, params)
116
+ view.frame = object_to_cg_rect(params, view, view.frame, view.rmq.grid)
117
+ @_previous_view = RubyMotionQuery::RMQ.weak_ref(view)
118
+ end
119
+
120
+ def update_view_bounds(view, params)
121
+ view.frame = object_to_cg_rect(params, view, view.bounds, view.rmq.grid)
122
+ @_previous_view = RubyMotionQuery::RMQ.weak_ref(view)
123
+ end
124
+
125
+ # The previous view is literally the last view that was layed out, not
126
+ # the sibling view, the last view layed out in this screen, etc, just
127
+ # the last view.
128
+ # As styles and layouts are always applied in order, and there is only
129
+ # 1 UI thread, this should work just fine
130
+ def previous_view
131
+ # TODO, verify that there is actually a problem with circular reference here
132
+ # if not, we can just store the view and not create a weakref
133
+ RubyMotionQuery::RMQ.weak_ref_value(@_previous_view)
134
+ end
135
+
136
+ def frame_for_view(view)
137
+ new(view.frame, view)
138
+ end
139
+
140
+ def bounds_for_view(view)
141
+ new(view.bounds, view)
142
+ end
143
+
144
+ # Used internally, don't use this
145
+ #
146
+ # In singleton for performance # TODO, test if this is necessary
147
+ def object_to_cg_rect(o, view = nil, existing_rect = nil, grid = nil)
148
+ if o.is_a?(Hash)
149
+ a = rect_hash_to_rect_array(view, existing_rect, o, grid)
150
+ CGRectMake(a[0], a[1], a[2], a[3])
151
+ elsif o == :full
152
+ if view
153
+ view.superview.bounds
154
+ else
155
+ rmq.rootview.bounds
156
+ end
157
+ elsif o.is_a?(RubyMotionQuery::Rect)
158
+ o.to_cgrect
159
+ elsif grid && o.is_a?(String)
160
+ a = rect_hash_to_rect_array(view, existing_rect, {grid: o}, grid)
161
+ CGRectMake(a[0], a[1], a[2], a[3])
162
+ elsif o.is_a?(Array)
163
+ case o.length
164
+ when 4
165
+ CGRectMake(o[0], o[1], o[2], o[3])
166
+ when 2
167
+ o
168
+ end
169
+ else
170
+ o # CGRect, etc
171
+ end
172
+ end
173
+
174
+ # Used internally, don't use this
175
+ #
176
+ # In singleton for performance # TODO, test if this is necessary
177
+ def rect_hash_to_rect_array(view, existing_rect, params, grid = nil)
178
+ # TODO check if this is performant, it should be
179
+ if (sv = view.superview) && (vc = view.rmq_data.view_controller)
180
+ not_in_root_view = !(vc.view == sv)
181
+ end
182
+
183
+ # Grid
184
+ if grid
185
+ if params_g = params[:grid] || params[:g]
186
+ params.delete(:grid)
187
+ params.delete(:g)
188
+
189
+ grid_h = grid[params_g]
190
+ params = grid_h.merge(params)
191
+ end
192
+ end
193
+
194
+ params_l = params[:l] || params[:left] || params[:x]
195
+ params_t = params[:t] || params[:top] || params[:y]
196
+ params_w = params[:w] || params[:width]
197
+ params_h = params[:h] || params[:height]
198
+
199
+ below_prev = params[:below_prev] || params[:bp] || params[:below_previous]
200
+ above_prev = params[:above_prev] || params[:ap] || params[:above_previous]
201
+ right_of_prev = params[:right_of_prev] || params[:rop] || params[:right_of_previous]
202
+ left_of_prev = params[:left_of_prev] || params[:lop] || params[:left_of_previous]
203
+
204
+ l = params_l || existing_rect.origin.x
205
+ t = params_t || existing_rect.origin.y
206
+ w = params_w || existing_rect.size.width
207
+ h = params_h || existing_rect.size.height
208
+
209
+ r = params[:r] || params[:right]
210
+ b = params[:b] || params[:bottom]
211
+
212
+ fr = params[:from_right] || params[:fr]
213
+ fb = params[:from_bottom] || params[:fb]
214
+
215
+ centered = params[:centered]
216
+
217
+ # Previous
218
+ if prev_view = previous_view
219
+ if params_g && (prev_sv = prev_view.superview)
220
+
221
+ previous_root_view_point = vc.view.convertPoint(prev_view.origin, fromView: prev_sv)
222
+
223
+ if below_prev
224
+ t = params_t = previous_root_view_point.y + prev_view.frame.size.height + below_prev
225
+ elsif above_prev
226
+ t = params_t = previous_root_view_point.y - above_prev - h
227
+ end
228
+
229
+ if right_of_prev
230
+ l = params_l = previous_root_view_point.x + prev_view.frame.size.width + right_of_prev
231
+ elsif left_of_prev
232
+ l = params_l = previous_root_view_point.x - left_of_prev - w
233
+ end
234
+
235
+ else
236
+ if below_prev
237
+ t = prev_view.frame.origin.y + prev_view.frame.size.height + below_prev
238
+ elsif above_prev
239
+ t = prev_view.frame.origin.y - above_prev - h
240
+ end
241
+
242
+ if right_of_prev
243
+ l = prev_view.frame.origin.x + prev_view.frame.size.width + right_of_prev
244
+ elsif left_of_prev
245
+ l = prev_view.frame.origin.x - left_of_prev - w
246
+ end
247
+ end
248
+ end
249
+
250
+ if sv
251
+ if (fr || fb || centered) # Needs size
252
+
253
+ # Horrible horrible hack, TODO fix. This is here because
254
+ # the root_view's height isn't changed until after viewDidLoad when
255
+ # vc.edgesForExtendedLayout = UIRectEdgeNone.
256
+ # Not sure how often people use UIRectEdgeNone as I never do,
257
+ # perhaps an edge case that should be isolated in some wayo
258
+ # I hate to have to check and calc this every time
259
+ if vc && !not_in_root_view && (vc.edgesForExtendedLayout == UIRectEdgeNone)
260
+ sv_size = CGSizeMake(sv.size.width, rmq.device.screen_height - 64)
261
+ else
262
+ sv_size = sv.size
263
+ end
264
+ end
265
+ end
266
+
267
+ # From right, from_bottom
268
+ if (fr || fb) && sv
269
+ if fr
270
+ if params_l || left_of_prev || right_of_prev
271
+ w = sv_size.width - l - fr
272
+ else
273
+ l = sv_size.width - w - fr
274
+ end
275
+ end
276
+
277
+ if fb
278
+ if params_t || below_prev || above_prev
279
+ h = sv_size.height - t - fb
280
+ else
281
+ t = sv_size.height - h - fb
282
+ end
283
+ end
284
+ end
285
+
286
+ # Right and bottom
287
+ if r && !fr && !params_l
288
+ l = r - w
289
+ end
290
+ if b && !fb && !params_t
291
+ t = b - h
292
+ end
293
+
294
+ # Left and right applied together
295
+ if params_l && r && !params_w
296
+ w = r - l
297
+ end
298
+ # Top and bottom applied together
299
+ if params_t && b && !params_h
300
+ h = b - t
301
+ end
302
+
303
+ # Centered, :horizontal, :vertical, :both
304
+ if sv && centered
305
+ case centered
306
+ when :horizontal
307
+ l = (sv_size.width / 2) - (w / 2)
308
+ when :vertical
309
+ t = (sv_size.height / 2) - (h / 2)
310
+ when :both
311
+ l = (sv_size.width / 2) - (w / 2)
312
+ t = (sv_size.height / 2) - (h / 2)
313
+ end
314
+ end
315
+
316
+ if padding = params[:padding] || params[:p]
317
+ if padding.is_a?(Hash)
318
+ padding_l = padding[:l] || padding[:left] || 0
319
+ padding_t = padding[:t] || padding[:top] || 0
320
+ padding_r = padding[:r] || padding[:right] || 0
321
+ padding_b = padding[:b] || padding[:bottom] || 0
322
+ l += padding_l
323
+ t += padding_t
324
+ w -= (padding_l + padding_r)
325
+ h -= (padding_t + padding_b)
326
+ else
327
+ l += padding
328
+ t += padding
329
+ w -= (padding * 2)
330
+ h -= (padding * 2)
331
+ end
332
+ end
333
+
334
+ if params_g && not_in_root_view # Change to root_view_space
335
+ point = CGPointMake(l,t)
336
+ root_view_point = vc.view.convertPoint(point, toView: sv)
337
+ l = root_view_point.x
338
+ t = root_view_point.y
339
+ end
340
+
341
+ [l,t,w,h]
342
+ end
343
+
344
+ end # << self
345
+
346
+ def initialize(params, view = nil, grid = nil)
347
+ @left = @top = @width = @height = 0
348
+ @view = view
349
+ update params, grid
350
+ end
351
+
352
+ def update(params, grid = nil)
353
+ # Doing all of the updates to the Rect in singleton for performance.
354
+ # It would be better to be done inside an actual Rect instance, but that
355
+ # would require creating a lot of temporary objects.
356
+ # TODO performance and see if there is any real loss bringing
357
+ # object_to_cg_rect into Rect instance
358
+ #
359
+ # If we did it that way, then we'd create a new instance, then appy the
360
+ # rect instance to the frame or bounds, like so:
361
+ # Rect.new(params, view, grid).apply_to_frame
362
+ cg_rect = RubyMotionQuery::Rect.object_to_cg_rect(params, @view, self.to_cgrect, grid)
363
+
364
+ @left = cg_rect.origin.x
365
+ @top = cg_rect.origin.y
366
+ @width = cg_rect.size.width
367
+ @height = cg_rect.size.height
368
+ end
369
+
370
+ def apply_to_frame
371
+ @view.frame = to_cgrect if @view
372
+ end
373
+ def apply_to_bounds
374
+ @view.bounds = to_cgrect if @view
375
+ end
376
+
377
+ def left
378
+ @left
379
+ end
380
+ alias :l :left
381
+ alias :x :left
382
+
383
+ def right
384
+ @left + @width
385
+ end
386
+ alias :r :right
387
+
388
+ def from_right
389
+ if @view && (sv = @view.superview)
390
+ sv.size.width - right
391
+ end
392
+ end
393
+ alias :fr :from_right
394
+
395
+ def top
396
+ @top
397
+ end
398
+ alias :t :top
399
+ alias :y :top
400
+
401
+ def bottom
402
+ @top + @height
403
+ end
404
+ alias :b :bottom
405
+
406
+ def from_bottom
407
+ if @view && (sv = @view.superview)
408
+ sv.size.height - bottom
409
+ end
410
+ end
411
+ alias :fb :from_bottom
412
+
413
+ def width
414
+ @width
415
+ end
416
+ alias :w :width
417
+
418
+ def height
419
+ @height
420
+ end
421
+ alias :h :height
422
+
423
+ def z_order
424
+ if @view
425
+ @view.superview.subviews.to_a.index(@view) # is there a better way??
426
+ end
427
+ end
428
+
429
+ def origin
430
+ to_cgpoint
431
+ end
432
+
433
+ def size
434
+ to_cgsize
435
+ end
436
+
437
+ def point_in_root_view
438
+ if @view && (sv = @view.superview) && (vc = view.rmq.view_controller)
439
+ vc.view.convertPoint(CGPointMake(@left,@top), fromView: sv)
440
+ end
441
+ end
442
+
443
+ def rect_in_root_view
444
+ if @view && (sv = @view.superview) && (vc = view.rmq.view_controller)
445
+ point = vc.view.convertPoint(CGPointMake(@left,@top), fromView: sv)
446
+ RubyMotionQuery::Rect.new({l: point.x, t: point.y, w: @view.size.width, h: @view.size.height}, @view)
447
+ end
448
+ end
449
+
450
+ def left_in_root_view
451
+ if point = point_in_root_view
452
+ point.x
453
+ end
454
+ end
455
+
456
+ def top_in_root_view
457
+ if point = point_in_root_view
458
+ point.y
459
+ end
460
+ end
461
+
462
+ # TODO add center
463
+
464
+ def z_position
465
+ if @view
466
+ @view.layer.zPosition
467
+ end
468
+ end
469
+
470
+ def to_cgpoint
471
+ CGPointMake(@left, @top)
472
+ end
473
+
474
+ def to_cgsize
475
+ CGSizeMake(@width, @height)
476
+ end
477
+
478
+ def to_cgrect
479
+ CGRectMake(@left, @top, @width, @height)
480
+ end
481
+ def to_a
482
+ [@left, @top, @width, @height]
483
+ end
484
+ def to_h
485
+ {left: @left, top: @top, width: @width, height: @height}
486
+ end
487
+
488
+ def inspect
489
+ format = '#0.#'
490
+ s = "Rect {l: #{RMQ.format.numeric(left, format)}"
491
+ s << ", t: #{RMQ.format.numeric(top, format)}"
492
+ s << ", w: #{RMQ.format.numeric(width, format)}"
493
+ s << ", h: #{RMQ.format.numeric(height, format)}}"
494
+ s
495
+ end
496
+
497
+ def log
498
+ def i_f_to_s(int_or_float)
499
+ if int_or_float % 1 == 0
500
+ int_or_float.to_i.to_s
501
+ else
502
+ int_or_float.to_s
503
+ end
504
+ end
505
+
506
+ l = i_f_to_s(left.round(2)).ljust(5)
507
+ t = i_f_to_s(top.round(2)).rjust(5)
508
+ w = i_f_to_s(width.round(2)).ljust(5)
509
+ h = i_f_to_s(height.round(2)).ljust(5)
510
+ b = i_f_to_s(bottom.round(2)).rjust(5)
511
+ r = i_f_to_s(right.round(2)).ljust(5)
512
+ fr = i_f_to_s(from_right.round(2)).ljust(5)
513
+ fb = i_f_to_s(from_bottom.round(2)).rjust(5)
514
+
515
+ ww = i_f_to_s(rmq.app.window.size.width.round(2))
516
+ wh = i_f_to_s(rmq.app.window.size.height.round(2))
517
+
518
+ if @view && (sv = @view.superview)
519
+ sw = i_f_to_s(sv.size.width.round(2))
520
+ sh = i_f_to_s(sv.size.height.round(2))
521
+ end
522
+
523
+ out = %(
524
+ *****************---*******---**************************
525
+ * | | * window
526
+ * #{ t} top | * {w: #{ww}, h: #{wh}}
527
+ * | | *
528
+ * --- | * superview
529
+ * ***************|***** --- * {w: #{sw}, h: #{sh}}
530
+ * * | * | *
531
+ * * | * | *
532
+ * * #{ b} bottom * | * view
533
+ * #{ l} * | * | * {l: #{l.strip}, t: #{t.strip},
534
+ *|-- left --|* | * | * w: #{w.strip}, h: #{h.strip}}
535
+ * * | * height #{ h} *
536
+ * * | * | * z_order: #{z_order}
537
+ * * #{ r} | * | * z_position: #{z_position}
538
+ *|------------------ right -+---|* | *
539
+ * * | * | #{fr} * Location in root view
540
+ * * | * |--+--from_right---|* {l: #{i_f_to_s(left_in_root_view)}, t: #{i_f_to_s(top_in_root_view)},
541
+ * * --- * | * w: #{w.strip}, h: #{h.strip}}
542
+ * ***************---*** --- *
543
+ * | *
544
+ * |------ width - + --| *
545
+ * #{ w} | *
546
+ * | *
547
+ * | *
548
+ * #{fb} from_bottom *
549
+ * | *
550
+ * --- *
551
+ ********************************************************
552
+ )
553
+ puts out
554
+ end
555
+
556
+ end
557
+ end
@@ -45,19 +45,32 @@ module RubyMotionQuery
45
45
  @view.setBackgroundImage value, forState: UIControlStateHighlighted
46
46
  end
47
47
 
48
- # Accepts UIEdgeInsetMake OR and array of values to be the inset.
48
+ # Accepts UIEdgeInsetsMake OR and array of values to be the inset.
49
49
  # st.title_edge_insets = UIEdgeInsetsMake(0, 10.0, 0, 0)
50
50
  # OR
51
51
  # st.title_edge_insets = [0, 10.0, 0, 0]
52
52
  def title_edge_insets=(value)
53
- inset = UIEdgeInsetsMake(value[0], value[1], value[2], value[3]) if value.is_a? Array
54
- @view.setTitleEdgeInsets(inset)
53
+ value = UIEdgeInsetsMake(value[0], value[1], value[2], value[3]) if value.is_a? Array
54
+ @view.setTitleEdgeInsets(value)
55
55
  end
56
56
 
57
57
  def title_edge_insets
58
58
  @view.titleEdgeInsets
59
59
  end
60
60
 
61
+ # Accepts UIEdgeInsetsMake OR and array of values to be the inset.
62
+ # st.image_edge_insets = UIEdgeInsetsMake(0, 10.0, 0, 0)
63
+ # OR
64
+ # st.image_edge_insets = [0, 10.0, 0, 0]
65
+ def image_edge_insets=(value)
66
+ value = UIEdgeInsetsMake(value[0], value[1], value[2], value[3]) if value.is_a? Array
67
+ @view.setImageEdgeInsets(value)
68
+ end
69
+
70
+ def image_edge_insets
71
+ @view.imageEdgeInsets
72
+ end
73
+
61
74
  end
62
75
  end
63
76
  end