hotcocoa 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +4 -3
- data/History.markdown +45 -0
- data/README.markdown +41 -41
- data/docs/HeatingUpWithHotCocoa-part1.markdown +189 -0
- data/docs/Mappings.markdown +1 -1
- data/docs/Resources.markdown +8 -8
- data/docs/Troubleshooting.markdown +15 -0
- data/docs/Tutorial.markdown +1 -1
- data/lib/hotcocoa/application/builder.rb +3 -1
- data/lib/hotcocoa/application/specification.rb +4 -3
- data/lib/hotcocoa/delegate_builder.rb +128 -99
- data/lib/hotcocoa/layout_view.rb +173 -107
- data/lib/hotcocoa/version.rb +1 -1
- data/template/__APPLICATION_NAME__.appspec +7 -4
- data/test/application/test_builder.rb +19 -6
- data/test/application/test_specification.rb +11 -6
- data/test/core_extensions/test_range.rb +9 -9
- data/test/test_delegate_builder.rb +64 -0
- metadata +14 -8
- data/History.txt +0 -29
@@ -1,120 +1,149 @@
|
|
1
|
-
|
2
|
-
# Builds a delegate for a control at runtime by creating a generic object
|
3
|
-
# and adding singleton methods for each given delegate method; it then
|
4
|
-
# tells the control to delegate to that created object.
|
5
|
-
class HotCocoa::DelegateBuilder
|
6
|
-
|
7
|
-
# @return The object that needs a delegate
|
8
|
-
attr_reader :control
|
9
|
-
|
10
|
-
# @return [Array<>] Delegate methods which are assumed to be implemented
|
11
|
-
# and therefore __MUST__ be given at least a stub
|
12
|
-
attr_reader :required_methods
|
13
|
-
|
14
|
-
# @return [Fixnum] Number of delegate methods that have been added
|
15
|
-
attr_reader :method_count
|
16
|
-
|
17
|
-
# @return [Object] The delegate object
|
18
|
-
attr_reader :delegate
|
19
|
-
|
20
|
-
# @param control the object which needs a delegate
|
21
|
-
# @param [Array<String>] required_methods
|
22
|
-
def initialize control, required_methods
|
23
|
-
@control = control
|
24
|
-
@required_methods = required_methods
|
25
|
-
@method_count = 0
|
26
|
-
@delegate = Object.new
|
27
|
-
end
|
1
|
+
module HotCocoa
|
28
2
|
|
29
3
|
##
|
30
|
-
#
|
31
|
-
|
32
|
-
|
4
|
+
# Builds a delegate for a control at runtime by creating a generic object
|
5
|
+
# and adding singleton methods for each given delegate method; it then
|
6
|
+
# tells the control to delegate to that created object.
|
7
|
+
class DelegateBuilder
|
8
|
+
|
9
|
+
##
|
10
|
+
# The object that needs a delegate
|
11
|
+
#
|
12
|
+
# @return
|
13
|
+
attr_reader :control
|
14
|
+
|
15
|
+
##
|
16
|
+
# Delegate methods which are assumed to be implemented and therefore
|
17
|
+
# __MUST__ be given at least a stub
|
18
|
+
#
|
19
|
+
# @return [Array<SEL,String>]
|
20
|
+
attr_reader :required_methods
|
21
|
+
|
22
|
+
##
|
23
|
+
# The delegate object
|
24
|
+
#
|
25
|
+
# @return [Object]
|
26
|
+
attr_reader :delegate
|
27
|
+
|
28
|
+
# @param control the object which needs a delegate
|
29
|
+
# @param [Array<SEL,String>] required_methods
|
30
|
+
def initialize control, required_methods
|
31
|
+
@control = control
|
32
|
+
@required_methods = required_methods
|
33
|
+
@delegate = Object.new
|
34
|
+
end
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
36
|
+
##
|
37
|
+
# Add a delegated method for {#control} to {#delegate}
|
38
|
+
def add_delegated_method block, selector_name, *parameters
|
39
|
+
clear_delegate if required_methods.empty?
|
37
40
|
|
38
|
-
|
39
|
-
end
|
41
|
+
delegate_method_builder.add_delegated_method block, selector_name, *parameters
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
control.send(method_name, &object.method(method_name)) if object.respond_to?(method_name)
|
43
|
+
required_methods.delete(selector_name)
|
44
|
+
set_delegate if required_methods.empty?
|
44
45
|
end
|
45
|
-
end
|
46
46
|
|
47
|
-
|
47
|
+
def delegate_to object, *method_names
|
48
|
+
method_names.each do |method_name|
|
49
|
+
control.send(method_name, &object.method(method_name)) if object.respond_to?(method_name)
|
50
|
+
end
|
51
|
+
end
|
48
52
|
|
49
|
-
def bind_block_to_delegate_instance_variable block
|
50
|
-
delegate.instance_variable_set(block_instance_variable, block)
|
51
|
-
end
|
52
53
|
|
53
|
-
|
54
|
-
# @todo GET RID OF EVAL
|
55
|
-
def create_delegate_method selector_name, parameters
|
56
|
-
required_methods.delete(selector_name)
|
57
|
-
eval %{
|
58
|
-
def delegate.#{parameterize_selector_name(selector_name)}
|
59
|
-
#{block_instance_variable}.call(#{parameter_values_for_mapping(selector_name, parameters)})
|
60
|
-
end
|
61
|
-
}
|
62
|
-
end
|
54
|
+
private
|
63
55
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
56
|
+
##
|
57
|
+
# Reset the delegate for {#control} to `nil`.
|
58
|
+
def clear_delegate
|
59
|
+
control.setDelegate(nil) if control.delegate
|
60
|
+
end
|
69
61
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
62
|
+
##
|
63
|
+
# Set the delegate for {#control} to {#delegate}
|
64
|
+
def set_delegate
|
65
|
+
control.setDelegate(delegate)
|
66
|
+
end
|
67
|
+
|
68
|
+
def delegate_method_builder
|
69
|
+
@delegate_method_builder ||= DelegateMethodBuilder.new(@delegate)
|
70
|
+
end
|
75
71
|
|
76
|
-
##
|
77
|
-
# Returns an instance variable name to be used for the delegate method
|
78
|
-
# currently being built.
|
79
|
-
#
|
80
|
-
# @return [String]
|
81
|
-
def block_instance_variable
|
82
|
-
"@block#{method_count}"
|
83
72
|
end
|
84
73
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
# parameterize_selector_name('myDelegateMethod') # => 'myDeletageMethod'
|
91
|
-
# parameterize_selector_name('myDelegateMethod:withArgument:') # => 'myDeletageMethod p1, withArgumnet:p2'
|
92
|
-
#
|
93
|
-
# @param [String] selector_name
|
94
|
-
# @return [String]
|
95
|
-
def parameterize_selector_name selector_name
|
96
|
-
return selector_name unless selector_name.include?(':')
|
97
|
-
|
98
|
-
params = selector_name.split(':')
|
99
|
-
result = "#{params.shift} p1"
|
100
|
-
params.each_with_index do |param, i|
|
101
|
-
result << ",#{param}:p#{i + 2}"
|
74
|
+
|
75
|
+
class DelegateMethodBuilder
|
76
|
+
|
77
|
+
def initialize target
|
78
|
+
@target = target
|
102
79
|
end
|
103
|
-
result
|
104
|
-
end
|
105
80
|
|
106
|
-
|
107
|
-
|
81
|
+
def add_delegated_method block, selector_name, *parameters
|
82
|
+
bind_block_to_delegate_instance_variable(selector_name, block)
|
83
|
+
create_delegate_method(selector_name, parameters)
|
84
|
+
end
|
85
|
+
|
86
|
+
def bind_block_to_delegate_instance_variable selector_name, block
|
87
|
+
@target.instance_variable_set(block_instance_variable_for(selector_name), block)
|
88
|
+
end
|
108
89
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
90
|
+
##
|
91
|
+
# @todo GET RID OF EVAL
|
92
|
+
def create_delegate_method selector_name, parameters
|
93
|
+
eval %{
|
94
|
+
def @target.#{parameterize_selector_name(selector_name)}
|
95
|
+
#{block_instance_variable_for(selector_name)}.call(#{parameter_values_for_mapping(selector_name, parameters)})
|
96
|
+
end
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Returns an instance variable name to be used for the delegate method
|
102
|
+
# currently being built.
|
103
|
+
#
|
104
|
+
# @return [String]
|
105
|
+
def block_instance_variable_for selector_name
|
106
|
+
"@block_#{selector_name.gsub(':', "_")}"
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Take an Objective-C selector and create a parameter list to be used
|
111
|
+
# in creating method's using #eval
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# parameterize_selector_name('myDelegateMethod') # => 'myDeletageMethod'
|
115
|
+
# parameterize_selector_name('myDelegateMethod:withArgument:') # => 'myDeletageMethod p1, withArgument:p2'
|
116
|
+
#
|
117
|
+
# @param [String] selector_name
|
118
|
+
# @return [String]
|
119
|
+
def parameterize_selector_name selector_name
|
120
|
+
return selector_name unless selector_name.include?(':')
|
121
|
+
|
122
|
+
params = selector_name.split(':')
|
123
|
+
result = "#{params.shift} p1"
|
124
|
+
params.each_with_index do |param, i|
|
125
|
+
result << ",#{param}:p#{i + 2}"
|
126
|
+
end
|
127
|
+
result
|
128
|
+
end
|
129
|
+
|
130
|
+
def parameter_values_for_mapping selector_name, parameters
|
131
|
+
return if parameters.empty?
|
132
|
+
|
133
|
+
result = []
|
134
|
+
selector_params = selector_name.split(':')
|
135
|
+
parameters.each do |parameter|
|
136
|
+
if (dot = parameter.index('.'))
|
137
|
+
result << "p#{selector_params.index(parameter[0...dot]) + 1}#{parameter[dot..-1]}"
|
138
|
+
else
|
139
|
+
parameter = parameter.to_s
|
140
|
+
raise "Error in delegate mapping: '#{parameter}' is not a valid parameter of method '#{selector_name}'" if selector_params.index(parameter).nil?
|
141
|
+
result << "p#{selector_params.index(parameter) + 1}"
|
142
|
+
end
|
116
143
|
end
|
144
|
+
result.join(', ')
|
117
145
|
end
|
118
|
-
|
146
|
+
|
119
147
|
end
|
148
|
+
|
120
149
|
end
|
data/lib/hotcocoa/layout_view.rb
CHANGED
@@ -203,8 +203,8 @@ module HotCocoa
|
|
203
203
|
end
|
204
204
|
|
205
205
|
def update_layout_views!
|
206
|
-
@view.superview.
|
207
|
-
@defaults_view.
|
206
|
+
@view.superview.relayout! if in_layout_view?
|
207
|
+
@defaults_view.relayout! if @defaults_view
|
208
208
|
end
|
209
209
|
|
210
210
|
private
|
@@ -214,36 +214,62 @@ module HotCocoa
|
|
214
214
|
end
|
215
215
|
end
|
216
216
|
|
217
|
+
##
|
218
|
+
# @todo Why aren't we mixing in {HotCocoa::Behaviors}?
|
219
|
+
#
|
220
|
+
# HotCocoa layout managing class. This class is responsible for keeping
|
221
|
+
# track of your UI layout, including adding, removing, and updating
|
222
|
+
# subviews.
|
217
223
|
class LayoutView < NSView
|
218
|
-
attr_accessor :frame_color
|
219
224
|
|
225
|
+
##
|
226
|
+
# Set some default values and call the super class initializer.
|
227
|
+
#
|
228
|
+
# @param [CGRect, Array<Number, Number, Number, Number>]
|
220
229
|
def initWithFrame frame
|
221
230
|
super
|
222
|
-
@mode
|
223
|
-
@spacing = 10
|
224
|
-
@margin
|
231
|
+
@mode = :vertical
|
232
|
+
@spacing = 10
|
233
|
+
@margin = 10
|
225
234
|
self
|
226
235
|
end
|
227
236
|
|
237
|
+
# @return [NSColor]
|
238
|
+
attr_accessor :frame_color
|
239
|
+
|
240
|
+
##
|
241
|
+
# Whether or not the layout mode is vertical.
|
228
242
|
def vertical?
|
229
243
|
@mode == :vertical
|
230
244
|
end
|
231
245
|
|
246
|
+
##
|
247
|
+
# Whether or not the layout mode is horizontal.
|
232
248
|
def horizontal?
|
233
249
|
@mode == :horizonal
|
234
250
|
end
|
235
251
|
|
236
|
-
|
237
|
-
|
238
|
-
|
252
|
+
##
|
253
|
+
# Set the layout mode. The default value is `:vertical`, you can
|
254
|
+
# change it to be `:horizontal` if you want.
|
255
|
+
#
|
256
|
+
# @param [Symbol]
|
257
|
+
def mode= new_mode
|
258
|
+
unless [:horizontal, :vertical].include?(new_mode)
|
259
|
+
raise ArgumentError, "invalid mode value #{new_mode}"
|
239
260
|
end
|
240
261
|
|
241
|
-
if
|
242
|
-
@mode =
|
262
|
+
if new_mode != @mode
|
263
|
+
@mode = new_mode
|
243
264
|
relayout!
|
244
265
|
end
|
245
266
|
end
|
246
267
|
|
268
|
+
##
|
269
|
+
# Set the default layout options. The options should follow the format
|
270
|
+
# that would be given to {HotCocoa::LayoutOptions}.
|
271
|
+
#
|
272
|
+
# @param [Hash]
|
247
273
|
def default_layout= options
|
248
274
|
options[:defaults_view] = self
|
249
275
|
@default_layout = LayoutOptions.new(nil, options)
|
@@ -251,74 +277,73 @@ module HotCocoa
|
|
251
277
|
end
|
252
278
|
|
253
279
|
def default_layout
|
254
|
-
@default_layout ||= LayoutOptions.new(nil, :
|
280
|
+
@default_layout ||= LayoutOptions.new(nil, defaults_view: self)
|
255
281
|
end
|
256
282
|
|
257
|
-
|
258
|
-
|
259
|
-
end
|
283
|
+
# @return [Fixnum]
|
284
|
+
attr_reader :spacing
|
260
285
|
|
261
|
-
|
262
|
-
|
263
|
-
|
286
|
+
##
|
287
|
+
# Change the spacing between subviews.
|
288
|
+
#
|
289
|
+
# @param [Number]
|
290
|
+
def spacing= new_spacing
|
291
|
+
if new_spacing != @spacing
|
292
|
+
@spacing = new_spacing.to_i
|
264
293
|
relayout!
|
265
294
|
end
|
266
295
|
end
|
267
296
|
|
268
|
-
|
269
|
-
|
270
|
-
end
|
271
|
-
|
272
|
-
def size= size
|
273
|
-
setFrameSize(size)
|
274
|
-
end
|
275
|
-
|
276
|
-
def margin
|
277
|
-
@margin
|
278
|
-
end
|
297
|
+
# @return [Fixnum]
|
298
|
+
attr_reader :margin
|
279
299
|
|
280
|
-
|
281
|
-
|
282
|
-
|
300
|
+
##
|
301
|
+
# Change the margin size for the view.
|
302
|
+
#
|
303
|
+
# @param [Fixnum]
|
304
|
+
def margin= new_margin
|
305
|
+
if new_margin != @margin
|
306
|
+
@margin = new_margin.to_i
|
283
307
|
relayout!
|
284
308
|
end
|
285
309
|
end
|
286
310
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
def
|
292
|
-
raise ArgumentError, "#{subview} is not a subview of #{self} and cannot be removed." unless subview.superview == self
|
293
|
-
options[:needs_display] == false ? subview.removeFromSuperviewWithoutNeedingDisplay : subview.removeFromSuperview
|
294
|
-
end
|
295
|
-
|
296
|
-
def addSubview(view)
|
311
|
+
##
|
312
|
+
# Add a new subview to the layout view.
|
313
|
+
#
|
314
|
+
# @param [NSView]
|
315
|
+
def addSubview view
|
297
316
|
super
|
298
|
-
if view.respond_to?
|
317
|
+
if view.respond_to? :layout
|
299
318
|
relayout!
|
300
319
|
else
|
301
320
|
raise ArgumentError, "view #{view} does not support the #layout method"
|
302
321
|
end
|
303
322
|
end
|
323
|
+
alias_method :<<, :addSubview
|
304
324
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
325
|
+
##
|
326
|
+
# Remove a subview from the layout.
|
327
|
+
#
|
328
|
+
# @param [NSView]
|
309
329
|
def remove_view view
|
310
|
-
unless subviews.include?
|
330
|
+
unless subviews.include? view
|
311
331
|
raise ArgumentError, "view #{view} not a subview of this LayoutView"
|
312
332
|
end
|
313
333
|
view.removeFromSuperview
|
314
334
|
relayout!
|
315
335
|
end
|
336
|
+
alias_method :remove, :remove_view
|
316
337
|
|
338
|
+
##
|
339
|
+
# Remove all the subviews from the layout view.
|
317
340
|
def remove_all_views
|
318
341
|
subviews.each { |view| view.removeFromSuperview }
|
319
342
|
relayout!
|
320
343
|
end
|
321
344
|
|
345
|
+
##
|
346
|
+
# This is a callback, you don't need to worry about it.
|
322
347
|
def drawRect frame
|
323
348
|
if frame_color
|
324
349
|
frame_color.set
|
@@ -326,59 +351,37 @@ module HotCocoa
|
|
326
351
|
end
|
327
352
|
end
|
328
353
|
|
354
|
+
##
|
355
|
+
# This is a callback, you don't need to worry about it.
|
329
356
|
def setFrame frame
|
330
357
|
super(frame, &nil)
|
331
358
|
relayout!
|
332
359
|
end
|
360
|
+
alias_method :frame=, :setFrame
|
333
361
|
|
362
|
+
##
|
363
|
+
# This is a callback, you don't need to worry about it.
|
334
364
|
def setFrameSize size
|
335
365
|
super(size, &nil)
|
336
366
|
relayout!
|
337
367
|
end
|
368
|
+
alias_method :size=, :setFrameSize
|
338
369
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
subviews.each do |view|
|
346
|
-
next unless can_layout?(view)
|
347
|
-
|
348
|
-
if vertical?
|
349
|
-
size = view.frameSize.height
|
350
|
-
expand = view.layout.expand_height?
|
351
|
-
padding = view.layout.top_padding + view.layout.bottom_padding
|
352
|
-
else
|
353
|
-
size = view.frameSize.width
|
354
|
-
expand = view.layout.expand_width?
|
355
|
-
padding = view.layout.left_padding + view.layout.right_padding
|
356
|
-
end
|
357
|
-
|
358
|
-
if expand
|
359
|
-
expandable_views += 1
|
360
|
-
else
|
361
|
-
expandable_size -= size
|
362
|
-
expandable_size -= @spacing
|
363
|
-
end
|
364
|
-
|
365
|
-
expandable_size -= padding
|
366
|
-
end
|
367
|
-
|
368
|
-
expandable_size /= expandable_views
|
369
|
-
expandable_size
|
370
|
-
end
|
371
|
-
|
370
|
+
##
|
371
|
+
# @todo This method could be optimized quite a bit, I think.
|
372
|
+
#
|
373
|
+
# Figure out how to layout all the subviews. This is the meat of the
|
374
|
+
# class.
|
372
375
|
def relayout!
|
373
|
-
view_size
|
374
|
-
end_dimension
|
376
|
+
view_size = frameSize
|
377
|
+
end_dimension = vertical? ? view_size.height : view_size.width
|
375
378
|
end_dimension -= (@margin * 2)
|
376
|
-
dimension
|
379
|
+
dimension = @margin
|
377
380
|
|
378
381
|
expandable_size = calc_expandable_size(end_dimension)
|
379
382
|
|
380
383
|
subviews.each do |view|
|
381
|
-
next unless can_layout?
|
384
|
+
next unless can_layout? view
|
382
385
|
|
383
386
|
options = view.layout
|
384
387
|
subview_size = view.frameSize
|
@@ -386,27 +389,27 @@ module HotCocoa
|
|
386
389
|
subview_dimension = vertical? ? subview_size.height : subview_size.width
|
387
390
|
|
388
391
|
if vertical?
|
389
|
-
primary_dimension
|
390
|
-
secondary_dimension =
|
391
|
-
primary_axis
|
392
|
-
secondary_axis
|
393
|
-
expand_primary
|
394
|
-
expand_secondary
|
395
|
-
padding_first
|
396
|
-
padding_second
|
397
|
-
padding_third
|
398
|
-
padding_fourth
|
392
|
+
primary_dimension = HEIGHT
|
393
|
+
secondary_dimension = WIDTH
|
394
|
+
primary_axis = X
|
395
|
+
secondary_axis = Y
|
396
|
+
expand_primary = EXPAND_HEIGHT
|
397
|
+
expand_secondary = EXPAND_WIDTH
|
398
|
+
padding_first = LEFT_PADDING
|
399
|
+
padding_second = RIGHT_PADDING
|
400
|
+
padding_third = BOTTOM_PADDING
|
401
|
+
padding_fourth = TOP_PADDING
|
399
402
|
else
|
400
|
-
primary_dimension
|
401
|
-
secondary_dimension =
|
402
|
-
primary_axis
|
403
|
-
secondary_axis
|
404
|
-
expand_primary
|
405
|
-
expand_secondary
|
406
|
-
padding_first
|
407
|
-
padding_second
|
408
|
-
padding_third
|
409
|
-
padding_fourth
|
403
|
+
primary_dimension = WIDTH
|
404
|
+
secondary_dimension = HEIGHT
|
405
|
+
primary_axis = Y
|
406
|
+
secondary_axis = X
|
407
|
+
expand_primary = EXPAND_WIDTH
|
408
|
+
expand_secondary = EXPAND_HEIGHT
|
409
|
+
padding_first = TOP_PADDING
|
410
|
+
padding_second = BOTTOM_PADDING
|
411
|
+
padding_third = LEFT_PADDING
|
412
|
+
padding_fourth = RIGHT_PADDING
|
410
413
|
end
|
411
414
|
|
412
415
|
view_frame.origin.send("#{primary_axis}=", @margin)
|
@@ -419,14 +422,13 @@ module HotCocoa
|
|
419
422
|
|
420
423
|
if options.send(expand_secondary)
|
421
424
|
view_frame.size.send("#{secondary_dimension}=",
|
422
|
-
view_size.send(
|
425
|
+
view_size.send(secondary_dimension) - (2 * @margin) -
|
423
426
|
options.send(padding_first) - options.send(padding_second))
|
424
427
|
else
|
425
428
|
|
426
429
|
case options.align
|
427
430
|
when :left, :bottom
|
428
431
|
# Nothing to do
|
429
|
-
|
430
432
|
when :center
|
431
433
|
view_frame.origin.send("#{primary_axis}=", (view_size.send(secondary_dimension) / 2.0) - (subview_size.send(secondary_dimension) / 2.0))
|
432
434
|
|
@@ -456,8 +458,72 @@ module HotCocoa
|
|
456
458
|
end
|
457
459
|
end
|
458
460
|
|
461
|
+
|
462
|
+
private
|
463
|
+
|
464
|
+
# @private
|
465
|
+
HEIGHT = 'height'
|
466
|
+
# @private
|
467
|
+
WIDTH = 'width'
|
468
|
+
# @private
|
469
|
+
X = 'x'
|
470
|
+
# @private
|
471
|
+
Y = 'y'
|
472
|
+
# @private
|
473
|
+
EXPAND_HEIGHT = 'expand_height?'
|
474
|
+
# @private
|
475
|
+
EXPAND_WIDTH = 'expand_width?'
|
476
|
+
# @private
|
477
|
+
LEFT_PADDING = 'left_padding'
|
478
|
+
# @private
|
479
|
+
RIGHT_PADDING = 'right_padding'
|
480
|
+
# @private
|
481
|
+
BOTTOM_PADDING = 'bottom_padding'
|
482
|
+
# @private
|
483
|
+
TOP_PADDING = 'top_padding'
|
484
|
+
|
485
|
+
##
|
486
|
+
# Calculate the maximum size that a subview can take up in the layout.
|
487
|
+
def calc_expandable_size end_dimension
|
488
|
+
expandable_size = end_dimension
|
489
|
+
expandable_views = 0
|
490
|
+
|
491
|
+
subviews.each do |view|
|
492
|
+
next unless can_layout? view
|
493
|
+
|
494
|
+
if vertical?
|
495
|
+
size = view.frameSize.height
|
496
|
+
expand = view.layout.expand_height?
|
497
|
+
padding = view.layout.top_padding + view.layout.bottom_padding
|
498
|
+
else
|
499
|
+
size = view.frameSize.width
|
500
|
+
expand = view.layout.expand_width?
|
501
|
+
padding = view.layout.left_padding + view.layout.right_padding
|
502
|
+
end
|
503
|
+
|
504
|
+
if expand
|
505
|
+
expandable_views += 1
|
506
|
+
else
|
507
|
+
expandable_size -= size
|
508
|
+
expandable_size -= @spacing
|
509
|
+
end
|
510
|
+
|
511
|
+
expandable_size -= padding
|
512
|
+
end
|
513
|
+
|
514
|
+
expandable_size /= expandable_views
|
515
|
+
expandable_size
|
516
|
+
end
|
517
|
+
|
518
|
+
##
|
519
|
+
# @note NSView defines `#layout` in Lion for AutoLayout, and so this
|
520
|
+
# will almost always return true on Lion, even if it should
|
521
|
+
# not. THIS IS A BUG.
|
522
|
+
#
|
523
|
+
# Whether or not the view can be used
|
459
524
|
def can_layout? view
|
460
525
|
view.respond_to?(:layout) && !view.layout.nil?
|
461
526
|
end
|
527
|
+
|
462
528
|
end
|
463
529
|
end
|
data/lib/hotcocoa/version.rb
CHANGED
@@ -12,6 +12,13 @@ Application::Specification.new do |s|
|
|
12
12
|
s.resources = Dir.glob('resources/**/*.*')
|
13
13
|
s.sources = Dir.glob('lib/**/*.rb')
|
14
14
|
|
15
|
+
# BridgeSupport is required if you need to run your app under OS X 10.6 (Snow Leopard)
|
16
|
+
# set this option to false if you do not wish to embed the BridgeSupport files during deployment
|
17
|
+
# (i.e. you are targeting Lion only)
|
18
|
+
# Information about BridgeSupport: http://www.macruby.org/documentation/tutorial.html
|
19
|
+
# Latest Preview of BridgeSupport: http://www.macruby.org/files/BridgeSupport%20Preview%203.zip
|
20
|
+
s.embed_bridgesupport = true
|
21
|
+
|
15
22
|
# optional copyright
|
16
23
|
# s.copyright = "2011, Your Company"
|
17
24
|
|
@@ -27,10 +34,6 @@ Application::Specification.new do |s|
|
|
27
34
|
# hotcocoa is automatically bundled and doesn't need to be specified here
|
28
35
|
# s.gems = ['rest-client']
|
29
36
|
|
30
|
-
# uncomment if you wish to embed the BridgeSupport files during deployment
|
31
|
-
# useful if you need to deploy the app to OS X 10.6.
|
32
|
-
# s.embed_bs = true
|
33
|
-
|
34
37
|
# uncomment to always make a clean build of the app
|
35
38
|
# s.overwrite = true
|
36
39
|
|