hotcocoa 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|