sugarcube 0.11.3 → 0.12

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.
@@ -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