sugarcube 0.11.3 → 0.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,11 @@
1
1
  class Numeric
2
+ def milliseconds
3
+ self / 1000.0
4
+ end
5
+ alias millisecond milliseconds
6
+ alias millisecs milliseconds
7
+ alias millisec milliseconds
8
+
2
9
  def seconds
3
10
  self
4
11
  end
@@ -0,0 +1,97 @@
1
+ if defined? NSLayoutConstraint
2
+ class NSLayoutConstraint
3
+ Relationships = {
4
+ equal: NSLayoutRelationEqual,
5
+ lte: NSLayoutRelationLessThanOrEqual,
6
+ gte: NSLayoutRelationGreaterThanOrEqual,
7
+ }
8
+ Attributes = {
9
+ left: NSLayoutAttributeLeft,
10
+ right: NSLayoutAttributeRight,
11
+ top: NSLayoutAttributeTop,
12
+ bottom: NSLayoutAttributeBottom,
13
+ leading: NSLayoutAttributeLeading,
14
+ trailing: NSLayoutAttributeTrailing,
15
+ width: NSLayoutAttributeWidth,
16
+ height: NSLayoutAttributeHeight,
17
+ center_x: NSLayoutAttributeCenterX,
18
+ center_y: NSLayoutAttributeCenterY,
19
+ baseline: NSLayoutAttributeBaseline,
20
+ }
21
+
22
+ def to_s
23
+ target = firstItem
24
+ relative_to = secondItem
25
+
26
+ if firstItem and secondItem
27
+ if secondItem == firstItem
28
+ relative_to = :self
29
+ elsif firstItem.superview and secondItem == firstItem.superview
30
+ relative_to = :superview
31
+ elsif secondItem.respond_to?(:stylename) and secondItem.stylename
32
+ relative_to = secondItem.stylename
33
+ end
34
+
35
+ if secondItem.superview and firstItem == secondItem.superview
36
+ target = :superview
37
+ elsif firstItem.respond_to?(:stylename) and firstItem.stylename
38
+ target = firstItem.stylename
39
+ end
40
+ elsif firstItem
41
+ if firstItem.respond_to?(:stylename) and firstItem.stylename
42
+ target = firstItem.stylename
43
+ end
44
+ elsif secondItem
45
+ if secondItem.respond_to?(:stylename) and secondItem.stylename
46
+ target = secondItem.stylename
47
+ end
48
+ end
49
+
50
+ op = case _to_s_relationship_reverse relation
51
+ when :equal
52
+ '=='
53
+ when :gte
54
+ '>='
55
+ when :lte
56
+ '<='
57
+ end
58
+ formula = 'first.'
59
+ formula << _to_s_attribute_reverse(firstAttribute).to_s
60
+ formula << ' ' << op << ' '
61
+ if multiplier != 1
62
+ formula << multiplier.to_s << ' × '
63
+ end
64
+ if secondItem
65
+ if firstItem == secondItem
66
+ formula << 'first.'
67
+ else
68
+ formula << 'second.'
69
+ end
70
+ formula << _to_s_attribute_reverse(secondAttribute).to_s
71
+ end
72
+ if constant != 0
73
+ if secondItem
74
+ formula << ' + '
75
+ end
76
+ formula << constant.to_s
77
+ end
78
+
79
+ return "#<#{self.class}:##{object_id.to_s(16)}" +
80
+ " firstItem=#{target.inspect}" +
81
+ " secondItem=#{relative_to.inspect}" +
82
+ " priority=#{priority.inspect}" +
83
+ " formula=#{formula.inspect}" +
84
+ ">"
85
+ end
86
+
87
+ private
88
+ def _to_s_attribute_reverse(attribute)
89
+ Attributes.key(attribute) || :none
90
+ end
91
+
92
+ def _to_s_relationship_reverse(relationship)
93
+ Relationships.key(relationship)
94
+ end
95
+
96
+ end
97
+ end
@@ -1,6 +1,10 @@
1
1
  class UILabel
2
2
 
3
3
  def to_s(options={})
4
+ text = self.text
5
+ if text && text.length > 20
6
+ text = text[0..20] + '...'
7
+ end
4
8
  super options.merge(inner: 'text: ' + text.inspect)
5
9
  end
6
10
 
@@ -0,0 +1,15 @@
1
+ class UITextField
2
+
3
+ def to_s(options={})
4
+ text = self.text
5
+ if text && text.length > 20
6
+ text = text[0..20] + '...'
7
+ end
8
+ placeholder = self.placeholder
9
+ if placeholder && placeholder.length > 20
10
+ placeholder = placeholder[0..20] + '...'
11
+ end
12
+ super options.merge(inner: {text: text, placeholder: placeholder, firstResponder?: firstResponder?})
13
+ end
14
+
15
+ end
@@ -7,8 +7,18 @@ class UIView
7
7
  else
8
8
  suffix = ''
9
9
  end
10
+ if options[:inner].is_a? Hash
11
+ inner = ''
12
+ options[:inner].each do |key, value|
13
+ inner += ', ' if inner.length > 0
14
+ inner += "#{key}: #{value.inspect}"
15
+ end
16
+ else
17
+ inner = options[:inner]
18
+ end
19
+
10
20
  "#{self.class.name}(##{self.object_id.to_s(16)}, #{SugarCube::Adjust::format_frame(self.frame)}" +
11
- (options[:inner] ? ', ' + options[:inner] : '') +
21
+ (inner ? ', ' + inner : '') +
12
22
  ')' +
13
23
  (options[:superview] && self.superview ? ", child of #{self.superview.class.name}(##{self.superview.object_id.to_s(16)})" : '') +
14
24
  suffix
@@ -0,0 +1,86 @@
1
+ class UIActionSheet
2
+
3
+ # UIActionSheet.alert("message",
4
+ # # The first button is considered the 'cancel' button, for the purposes of
5
+ # # whether the cancel or success handler gets called, the second button is
6
+ # # the 'destructive' button, and the rest are plain old buttons.
7
+ # buttons: %w"Cancel OK No-way",
8
+ # cancel: proc{ puts "nevermind" },
9
+ # destructive: proc{ puts "OHHH YEAAH!" },
10
+ # success: proc{ |pressed| puts "pressed: #{pressed}" },
11
+ # )
12
+ def self.alert(title, options={}, &block)
13
+ if options.is_a? String
14
+ options = {message: options}
15
+ end
16
+
17
+ # create the delegate
18
+ delegate = SugarCube::ActionSheetDelegate.new
19
+ delegate.on_success = options[:success] || block
20
+ delegate.on_destructive = options[:destructive] || block
21
+ delegate.on_cancel = options[:cancel]
22
+ delegate.send(:retain)
23
+
24
+ args = [title] # initWithTitle:
25
+ args << options[:message] # message:
26
+ args << delegate # delegate:
27
+
28
+ buttons = options[:buttons] || []
29
+ if buttons.empty?
30
+ # cancelButtonTitle: is first, so check for cancel
31
+ buttons << "Cancel" if options[:cancel]
32
+ # destructiveButtonTitle: is first, so check for cancel
33
+ buttons << "Cancel" if options[:cancel]
34
+ # otherButtonTitles:
35
+ buttons << "OK" if options[:success] or buttons.empty?
36
+ elsif buttons.length == 1 and options[:cancel]
37
+ raise "If you only have one button, use a :success handler, not :cancel (and definitely not BOTH)"
38
+ end
39
+
40
+ # the button titles. These are passed to the success handler.
41
+ delegate.buttons = buttons
42
+
43
+ # uses localized buttons in the actual alert
44
+ args.concat(buttons.map{ |s| s.localized })
45
+ args << nil # otherButtonTitles:..., nil
46
+
47
+ alert = self.alloc
48
+ alert.send('initWithTitle:message:delegate:cancelButtonTitle:destructiveButtonTitle:otherButtonTitles:', *args)
49
+ alert.show
50
+ alert
51
+ end
52
+
53
+ private
54
+ def dummy
55
+ self.initWithTitle(nil, message:nil, delegate:nil, cancelButtonTitle:nil, destructiveButtonTitle:nil, otherButtonTitles:nil)
56
+ end
57
+
58
+ end
59
+
60
+
61
+ module SugarCube
62
+ class ActionSheetDelegate
63
+ attr_accessor :buttons
64
+ attr_accessor :on_cancel
65
+ attr_accessor :on_destructive
66
+ attr_accessor :on_success
67
+
68
+ def alertSheet(alert, didDismissWithButtonIndex:index)
69
+ if index == alert.destructiveButtonIndex && on_destructive
70
+ on_destructive.call
71
+ elsif index == alert.cancelButtonIndex && on_cancel
72
+ on_cancel.call
73
+ elsif on_success
74
+ if on_success.arity == 0
75
+ on_success.call
76
+ else
77
+ button = buttons[index]
78
+ on_success.call(button)
79
+ end
80
+ end
81
+
82
+ self.send(:autorelease)
83
+ end
84
+
85
+ end
86
+ end
@@ -61,7 +61,7 @@ module SugarCube
61
61
  attr_accessor :on_success
62
62
 
63
63
  def alertView(alert, didDismissWithButtonIndex:index)
64
- if index == 0 && on_cancel
64
+ if index == alert.cancelButtonIndex && on_cancel
65
65
  on_cancel.call
66
66
  elsif on_success
67
67
  if on_success.arity == 0
@@ -1,6 +1,6 @@
1
-
2
1
  class UIColor
3
2
  def uicolor ; self ; end
3
+ alias cgcolor CGColor
4
4
 
5
5
  def red
6
6
  _sugarcube_colors[:red]
@@ -24,15 +24,24 @@ private
24
24
  red = Pointer.new(:float)
25
25
  green = Pointer.new(:float)
26
26
  blue = Pointer.new(:float)
27
+ white = Pointer.new(:float)
27
28
  alpha = Pointer.new(:float)
28
- self.getRed(red, green:green, blue:blue, alpha:alpha)
29
- @color = {
30
- red: red[0],
31
- green: green[0],
32
- blue: blue[0],
33
- alpha: alpha[0],
34
- }
29
+ if self.getRed(red, green:green, blue:blue, alpha:alpha)
30
+ {
31
+ red: red[0],
32
+ green: green[0],
33
+ blue: blue[0],
34
+ alpha: alpha[0],
35
+ }
36
+ elsif self.getWhite(white, alpha:alpha)
37
+ {
38
+ red: white[0],
39
+ green: white[0],
40
+ blue: white[0],
41
+ alpha: alpha[0],
42
+ }
43
+ end
35
44
  end
36
45
  end
37
46
 
38
- end
47
+ end
@@ -1,6 +1,7 @@
1
1
  class UIImage
2
2
  def uiimage ; self ; end
3
3
 
4
+ # @return [UIColor]
4
5
  def uicolor(alpha=nil)
5
6
  color = UIColor.colorWithPatternImage(self)
6
7
  if not alpha.nil?
@@ -10,19 +11,89 @@ class UIImage
10
11
  color
11
12
  end
12
13
 
14
+ # @return [UIImageView]
13
15
  def uiimageview
14
- @uiimageview = UIImageView.alloc.initWithImage(self)
16
+ UIImageView.alloc.initWithImage(self)
17
+ end
18
+
19
+ # @return [NSData] an NSData object in PNG format
20
+ def nsdata
21
+ UIImagePNGRepresentation(self)
15
22
  end
16
23
 
17
24
  ##|
18
25
  ##| REALLY HANDY STUFF!
26
+ ##| many of these methods are translated from:
27
+ ##| <http://www.catamount.com/blog/uiimage-extensions-for-cutting-scaling-and-rotating-uiimages/>
28
+ ##| <http://www.catamount.com/forums/viewtopic.php?f=21&t=967>
19
29
  ##|
20
- def scale_to new_size
21
- UIGraphicsBeginImageContextWithOptions(new_size, false, 0.0)
22
- self.drawInRect([[0, 0], new_size])
23
- image = UIGraphicsGetImageFromCurrentImageContext()
30
+ def in_rect(rect)
31
+ # not necessary, since we don't modify/examine the rect
32
+ # rect = SugarCube::CoreGraphics::Rect(rect)
33
+ imageRef = CGImageCreateWithImageInRect(self.CGImage, rect)
34
+ sub_image = UIImage.imageWithCGImage(imageRef)
35
+
36
+ return sub_image
37
+ end
38
+
39
+ def scale_to(new_size)
40
+ new_size = SugarCube::CoreGraphics::Size(new_size)
41
+
42
+ sourceImage = self
43
+ newImage = nil
44
+
45
+ image_size = sourceImage.size
46
+ width = image_size.width
47
+ height = image_size.height
48
+
49
+ target_width = new_size.width
50
+ target_height = new_size.height
51
+
52
+ scale_factor = 0.0
53
+ scaled_width = target_width
54
+ scaled_height = target_height
55
+
56
+ thumbnail_point = CGPoint.new(0.0, 0.0)
57
+
58
+ unless CGSizeEqualToSize(image_size, new_size)
59
+ width_factor = target_width / width
60
+ heightFactor = target_height / height
61
+
62
+ if width_factor < heightFactor
63
+ scale_factor = width_factor
64
+ else
65
+ scale_factor = heightFactor
66
+ end
67
+
68
+ scaled_width = width * scale_factor
69
+ scaled_height = height * scale_factor
70
+
71
+ # center the image
72
+
73
+ if width_factor < heightFactor
74
+ thumbnail_point.y = (target_height - scaled_height) * 0.5
75
+ elsif width_factor > heightFactor
76
+ thumbnail_point.x = (target_width - scaled_width) * 0.5
77
+ end
78
+ end
79
+
80
+ # this is actually the interesting part:
81
+
82
+ UIGraphicsBeginImageContext(new_size)
83
+
84
+ thumbnail_rect = CGRectZero
85
+ thumbnail_rect.origin = thumbnail_point
86
+ thumbnail_rect.size.width = scaled_width
87
+ thumbnail_rect.size.height = scaled_height
88
+
89
+ sourceImage.drawInRect(thumbnail_rect)
90
+
91
+ new_image = UIGraphicsGetImageFromCurrentImageContext()
24
92
  UIGraphicsEndImageContext()
25
- return image
93
+
94
+ raise "could not scale image" unless new_image
95
+
96
+ return new_image
26
97
  end
27
98
 
28
99
  def rounded(corner_radius=5)
@@ -30,18 +101,93 @@ class UIImage
30
101
  path = UIBezierPath.bezierPathWithRoundedRect([[0, 0], size], cornerRadius:corner_radius)
31
102
  path.addClip
32
103
  self.drawInRect([[0, 0], size])
33
- image = UIGraphicsGetImageFromCurrentImageContext()
104
+ new_image = UIGraphicsGetImageFromCurrentImageContext()
105
+ UIGraphicsEndImageContext()
106
+ return new_image
107
+ end
34
108
 
109
+ # Accepts two options: brightness (default: -0.5) and saturation (default: -0.2)
110
+ # Returns a darkened version of the image.
111
+ def darken(options={})
112
+ filter_name = 'CIColorControls'
113
+ filter_options = {
114
+ inputBrightness: options[:brightness] || -0.5,
115
+ inputSaturation: options[:saturation] || -0.2,
116
+ }
117
+
118
+ cg_input_image = CIImage.alloc.initWithImage(self)
119
+ darken_filter = CIFilter.filterWithName(filter_name)
120
+ raise Exception.new("Filter not found: #{filter_name}") unless darken_filter
121
+
122
+ darken_filter.setDefaults
123
+ darken_filter.setValue(cg_input_image, forKey:'inputImage')
124
+ filter_options.each_pair do |key, value|
125
+ darken_filter.setValue(value, forKey:key)
126
+ end
127
+ output = darken_filter.valueForKey('outputImage')
128
+
129
+ context = CIContext.contextWithOptions(nil)
130
+ cg_output_image = context.createCGImage(output, fromRect:output.extent)
131
+ output_image = UIImage.imageWithCGImage(cg_output_image)
132
+
133
+ return output_image
134
+ end
135
+
136
+ ##|
137
+ ##| rotate images
138
+ ##|
139
+ def rotate(angle_or_direction)
140
+ case angle_or_direction
141
+ when :left
142
+ radian = -90.degrees
143
+ when :right
144
+ radian = 90.degrees
145
+ when :flip
146
+ radian = 180.degrees
147
+ when Numeric
148
+ radian = angle_or_direction
149
+ else
150
+ raise "Unknown angle/direction #{angle_or_direction.inspect}"
151
+ end
152
+
153
+ w = (self.size.width * Math.cos(radian)).abs + (self.size.height * Math.sin(radian)).abs
154
+ h = (self.size.height * Math.cos(radian)).abs + (self.size.width * Math.sin(radian)).abs
155
+ new_size = CGSize.new(w, h)
156
+ new_size = self.size
157
+
158
+ # Create the bitmap context
159
+ UIGraphicsBeginImageContext(new_size)
160
+ bitmap = UIGraphicsGetCurrentContext()
161
+
162
+ # Move the origin to the middle of the image so we will rotate and scale around the center.
163
+ CGContextTranslateCTM(bitmap, new_size.width / 2, new_size.height / 2)
164
+
165
+ # Rotate the image context
166
+ CGContextRotateCTM(bitmap, radian)
167
+
168
+ # otherwise it'll be upside down:
169
+ CGContextScaleCTM(bitmap, 1.0, -1.0)
170
+ # Now, draw the rotated/scaled image into the context
171
+ CGContextDrawImage(bitmap, CGRectMake(-new_size.width / 2, -new_size.height / 2, new_size.width, new_size.height), self.CGImage)
172
+
173
+ new_image = UIGraphicsGetImageFromCurrentImageContext()
35
174
  UIGraphicsEndImageContext()
36
- return image
175
+ return new_image
37
176
  end
38
177
 
39
- def tileable
40
- resizableImageWithCapInsets(UIEdgeInsetsZero, resizingMode:UIImageResizingModeTile)
178
+ ##|
179
+ ##| resizableImageWithCapInsets
180
+ ##|
181
+ def tileable(insets=UIEdgeInsetsZero)
182
+ # not necessary, since we don't modify/examine the insets
183
+ # insets = SugarCube::CoreGraphics::EdgeInsets(insets)
184
+ resizableImageWithCapInsets(insets, resizingMode:UIImageResizingModeTile)
41
185
  end
42
186
 
43
- def stretchable
44
- resizableImageWithCapInsets(UIEdgeInsetsZero, resizingMode:UIImageResizingModeStretch)
187
+ def stretchable(insets=UIEdgeInsetsZero)
188
+ # not necessary, since we don't modify/examine the insets
189
+ # insets = SugarCube::CoreGraphics::EdgeInsets(insets)
190
+ resizableImageWithCapInsets(insets, resizingMode:UIImageResizingModeStretch)
45
191
  end
46
192
 
47
193
  end