motion-xray 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +21 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +28 -0
- data/README.md +426 -0
- data/Rakefile +11 -0
- data/app/app_delegate.rb +44 -0
- data/lib/motion-xray.rb +37 -0
- data/lib/motion-xray/plugins/accessibility_plugin.rb +129 -0
- data/lib/motion-xray/plugins/log_plugin.rb +301 -0
- data/lib/motion-xray/plugins/save_ui_plugin.rb +142 -0
- data/lib/motion-xray/plugins/ui_plugin.rb +41 -0
- data/lib/motion-xray/version.rb +5 -0
- data/lib/motion-xray/views/xray_color_swatch.rb +234 -0
- data/lib/motion-xray/views/xray_dpad.rb +142 -0
- data/lib/motion-xray/views/xray_gradient_view.rb +23 -0
- data/lib/motion-xray/views/xray_headers.rb +101 -0
- data/lib/motion-xray/views/xray_lock_button.rb +50 -0
- data/lib/motion-xray/views/xray_scroll_view.rb +12 -0
- data/lib/motion-xray/views/xray_toolbar.rb +173 -0
- data/lib/motion-xray/views/xray_window.rb +13 -0
- data/lib/motion-xray/xray.rb +56 -0
- data/lib/motion-xray/xray_constants.rb +5 -0
- data/lib/motion-xray/xray_dummy.rb +8 -0
- data/lib/motion-xray/xray_editors.rb +62 -0
- data/lib/motion-xray/xray_ext.rb +125 -0
- data/lib/motion-xray/xray_plugin.rb +40 -0
- data/lib/motion-xray/xray_typewriter.rb +217 -0
- data/lib/motion-xray/xray_ui.rb +723 -0
- data/lib/motion-xray/z_editors/xray_boolean_editor.rb +24 -0
- data/lib/motion-xray/z_editors/xray_color_editor.rb +119 -0
- data/lib/motion-xray/z_editors/xray_frame_editor.rb +108 -0
- data/lib/motion-xray/z_editors/xray_image_editor.rb +78 -0
- data/lib/motion-xray/z_editors/xray_text_editor.rb +94 -0
- data/lib/resources/xray_button_bg@2x.png +0 -0
- data/lib/resources/xray_choose_button@2x.png +0 -0
- data/lib/resources/xray_clear_button@2x.png +0 -0
- data/lib/resources/xray_detail_button@2x.png +0 -0
- data/lib/resources/xray_dpad@2x.png +0 -0
- data/lib/resources/xray_dpad_center@2x.png +0 -0
- data/lib/resources/xray_dpad_down@2x.png +0 -0
- data/lib/resources/xray_dpad_left@2x.png +0 -0
- data/lib/resources/xray_dpad_right@2x.png +0 -0
- data/lib/resources/xray_dpad_up@2x.png +0 -0
- data/lib/resources/xray_drawer_left@2x.png +0 -0
- data/lib/resources/xray_drawer_right@2x.png +0 -0
- data/lib/resources/xray_edit_button@2x.png +0 -0
- data/lib/resources/xray_email_button@2x.png +0 -0
- data/lib/resources/xray_lock_button_horizontal@2x.png +0 -0
- data/lib/resources/xray_lock_button_locked@2x.png +0 -0
- data/lib/resources/xray_lock_button_unlocked@2x.png +0 -0
- data/lib/resources/xray_lock_button_vertical@2x.png +0 -0
- data/motion-xray.gemspec +40 -0
- data/resources/Default-568h@2x.png +0 -0
- data/spec/xray_view_spec.rb +43 -0
- data/vendor/Podfile.lock +11 -0
- metadata +177 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
module Motion ; module Xray
|
2
|
+
|
3
|
+
class SaveUIPlugin < Plugin
|
4
|
+
name 'Save UI'
|
5
|
+
|
6
|
+
def initialize(type=nil)
|
7
|
+
@type = type
|
8
|
+
end
|
9
|
+
|
10
|
+
def type
|
11
|
+
@type || :teacup
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# @param format [Symbol] :teacup or :pixate
|
17
|
+
# @param type [Class] The class that this encoder can handle
|
18
|
+
# @block handler This block should accept an instance (of `type`) and return a string.
|
19
|
+
def register(format, type, &handler)
|
20
|
+
# don't care about the return - the side effect is to establish
|
21
|
+
# @encoders
|
22
|
+
encoders(format)[type] = handler
|
23
|
+
end
|
24
|
+
|
25
|
+
def encoders(format, type=nil)
|
26
|
+
@encoders ||= {}
|
27
|
+
@encoders[format] ||= {}
|
28
|
+
unless @did_startup
|
29
|
+
@did_startup = true
|
30
|
+
startup
|
31
|
+
end
|
32
|
+
|
33
|
+
if type
|
34
|
+
retval = nil
|
35
|
+
while type
|
36
|
+
retval = @encoders[format][type]
|
37
|
+
break if retval
|
38
|
+
type = type.superclass
|
39
|
+
end
|
40
|
+
return retval
|
41
|
+
else
|
42
|
+
return @encoders[format]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def encode(format, object)
|
47
|
+
handler = encoders(format, object.class)
|
48
|
+
if handler
|
49
|
+
handler.call(object)
|
50
|
+
else
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def startup
|
56
|
+
register(:teacup, CGRect) { |rect| "[[#{rect.origin.x}, #{rect.origin.y}], [#{rect.size.width}, #{rect.size.height}]]"}
|
57
|
+
register(:teacup, CGPoint) { |rect| "[#{rect.origin.x}, #{rect.origin.y}]"}
|
58
|
+
register(:teacup, CGSize) { |rect| "[#{rect.size.width}, #{rect.size.height}]"}
|
59
|
+
register(:teacup, true.class) { |t| 'true' }
|
60
|
+
register(:teacup, false.class) { |t| 'false' }
|
61
|
+
# fall back
|
62
|
+
register(:teacup, NSObject) { |v| v.inspect }
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize
|
68
|
+
# uiview instance => list of changes
|
69
|
+
@changes = {}
|
70
|
+
end
|
71
|
+
|
72
|
+
def plugin_view(canvas)
|
73
|
+
@log = UITextView.alloc.initWithFrame(canvas.bounds)
|
74
|
+
@log.editable = false
|
75
|
+
@log.font = :monospace.uifont
|
76
|
+
@log.textColor = 0xBCBEAB.uicolor
|
77
|
+
@log.backgroundColor = 0x2b2b2b.uicolor
|
78
|
+
return @log
|
79
|
+
end
|
80
|
+
|
81
|
+
def save_changes(notification)
|
82
|
+
@changes[@target] ||= {}
|
83
|
+
property = notification.userInfo['property']
|
84
|
+
value = notification.userInfo['value']
|
85
|
+
original = notification.userInfo['original']
|
86
|
+
|
87
|
+
if value == original
|
88
|
+
@changes[@target].delete(property)
|
89
|
+
else
|
90
|
+
@changes[@target][property] = notification.userInfo['value']
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def edit(target)
|
95
|
+
super
|
96
|
+
XrayTargetDidChangeNotification.remove_observer(self)
|
97
|
+
XrayTargetDidChangeNotification.add_observer(self, :'save_changes:', @target)
|
98
|
+
end
|
99
|
+
|
100
|
+
def show
|
101
|
+
if type
|
102
|
+
@log.text = send("#{type}_text")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def teacup_text
|
107
|
+
apply = {}
|
108
|
+
@changes.each do |view, properties|
|
109
|
+
properties.each do |property, value|
|
110
|
+
encoded = SaveUIPlugin.encode(:teacup, value)
|
111
|
+
if encoded
|
112
|
+
apply[view] ||= []
|
113
|
+
apply[view] << [property, encoded]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
text = ''
|
119
|
+
apply.each do |view, stuff|
|
120
|
+
first_line = true
|
121
|
+
if view.stylesheet && view.stylename
|
122
|
+
name = "Teacup::Stylesheet[#{view.stylesheet.name.inspect}].style #{view.stylename.inspect},\n "
|
123
|
+
else
|
124
|
+
name = "#{view.class.name.downcase}.style "
|
125
|
+
end
|
126
|
+
|
127
|
+
text << "#{name}"
|
128
|
+
stuff.each do |property, encoded|
|
129
|
+
unless first_line
|
130
|
+
text << ",\n "
|
131
|
+
end
|
132
|
+
text << "#{property}: #{encoded}"
|
133
|
+
first_line = false
|
134
|
+
end
|
135
|
+
text << "\n\n"
|
136
|
+
end
|
137
|
+
return text
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Motion ; module Xray
|
2
|
+
|
3
|
+
class UIPlugin < Plugin
|
4
|
+
name 'UI'
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@editor_instances = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def plugin_view(canvas)
|
11
|
+
@editors = XrayTypewriterView.alloc.initWithFrame(canvas.bounds)
|
12
|
+
@editors.scroll_view = canvas
|
13
|
+
@editors
|
14
|
+
end
|
15
|
+
|
16
|
+
def edit(target)
|
17
|
+
super
|
18
|
+
@editors.subviews.each &:removeFromSuperview
|
19
|
+
@editor_instances = []
|
20
|
+
|
21
|
+
properties = @target.xray
|
22
|
+
sections = properties.keys
|
23
|
+
properties.each do |section, editors|
|
24
|
+
section_view = XraySectionHeader.alloc.initWithFrame([[0, 0], [Xray.ui.full_screen_width, 20]])
|
25
|
+
section_view.text = section
|
26
|
+
@editors << section_view
|
27
|
+
editors.each do |property, editor_class|
|
28
|
+
next unless editor_class
|
29
|
+
|
30
|
+
editor_instance = editor_class.with_target(@target, property:property)
|
31
|
+
@editor_instances << editor_instance
|
32
|
+
section_view.tracking_view << editor_instance.get_edit_view(@editors.bounds.size.width)
|
33
|
+
end
|
34
|
+
@editors << section_view.tracking_view
|
35
|
+
end
|
36
|
+
@editors.layoutIfNeeded
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
module Motion ; module Xray
|
2
|
+
|
3
|
+
class XrayColorSwatch < UIControl
|
4
|
+
|
5
|
+
def initWithFrame(frame)
|
6
|
+
super.tap do
|
7
|
+
gradient_view = XrayGradientView.alloc.initWithFrame(self.bounds).tap do |gradient_view|
|
8
|
+
gradient_view.layer.borderWidth = 1
|
9
|
+
gradient_view.layer.borderColor = :gray.uicolor.CGColor
|
10
|
+
gradient_view.userInteractionEnabled = false
|
11
|
+
end
|
12
|
+
self << gradient_view
|
13
|
+
|
14
|
+
swatch_rect = gradient_view.bounds.shrink(3)
|
15
|
+
|
16
|
+
gradient_view << XrayTriangleSwatch.alloc.initWithFrame(swatch_rect)
|
17
|
+
|
18
|
+
@color_swatch = UIView.alloc.initWithFrame(swatch_rect).tap do |color_swatch|
|
19
|
+
color_swatch.layer.borderWidth = 1
|
20
|
+
color_swatch.layer.borderColor = :dimgray.uicolor.CGColor
|
21
|
+
end
|
22
|
+
gradient_view << @color_swatch
|
23
|
+
|
24
|
+
@pressed_shader = UIView.alloc.initWithFrame(self.bounds).tap do |pressed_shader|
|
25
|
+
pressed_shader.backgroundColor = :black.uicolor(0.5)
|
26
|
+
pressed_shader.hide
|
27
|
+
end
|
28
|
+
self << @pressed_shader
|
29
|
+
|
30
|
+
self.on :touch_start do
|
31
|
+
@pressed_shader.show
|
32
|
+
end
|
33
|
+
self.on :touch_stop do
|
34
|
+
@pressed_shader.hide
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def color=(value)
|
40
|
+
@color_swatch.backgroundColor = value && value.uicolor
|
41
|
+
end
|
42
|
+
|
43
|
+
def color
|
44
|
+
@color_swatch.backgroundColor
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class XrayTriangleSwatch < UIView
|
50
|
+
|
51
|
+
def drawRect(rect)
|
52
|
+
context = UIGraphicsGetCurrentContext()
|
53
|
+
|
54
|
+
path = UIBezierPath.bezierPath
|
55
|
+
path.moveToPoint(bounds.top_right)
|
56
|
+
path.addLineToPoint(bounds.bottom_right)
|
57
|
+
path.addLineToPoint(bounds.bottom_left)
|
58
|
+
CGContextAddPath(context, path.CGPath)
|
59
|
+
:black.uicolor.setFill
|
60
|
+
CGContextFillPath(context)
|
61
|
+
|
62
|
+
path = UIBezierPath.bezierPath
|
63
|
+
path.moveToPoint(bounds.bottom_left)
|
64
|
+
path.addLineToPoint(bounds.top_left)
|
65
|
+
path.addLineToPoint(bounds.top_right)
|
66
|
+
CGContextAddPath(context, path.CGPath)
|
67
|
+
:white.uicolor.setFill
|
68
|
+
CGContextFillPath(context)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
class XrayColorSliders < UIControl
|
74
|
+
attr_reader :color
|
75
|
+
|
76
|
+
def initWithFrame(frame)
|
77
|
+
super.tap do
|
78
|
+
self.backgroundColor = :black.uicolor
|
79
|
+
@color = :clear.uicolor
|
80
|
+
@triangle = XrayTriangleSwatch.alloc.initWithFrame(CGRect.empty)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def color=(value)
|
85
|
+
@color = value.uicolor
|
86
|
+
setNeedsDisplay
|
87
|
+
end
|
88
|
+
|
89
|
+
def drawRect(rect)
|
90
|
+
super
|
91
|
+
|
92
|
+
r = color.red
|
93
|
+
g = color.green
|
94
|
+
b = color.blue
|
95
|
+
a = color.alpha
|
96
|
+
return unless r && g && b && a
|
97
|
+
|
98
|
+
context = UIGraphicsGetCurrentContext()
|
99
|
+
color_space = CGColorSpaceCreateDeviceRGB()
|
100
|
+
slider_height = bounds.height / 5
|
101
|
+
slider_size = bounds.size
|
102
|
+
slider_size.height = slider_height
|
103
|
+
path = UIBezierPath.bezierPathWithRect([[0, 0], slider_size])
|
104
|
+
@triangle.frame = [[0, 0], slider_size]
|
105
|
+
@triangle.drawRect(rect)
|
106
|
+
|
107
|
+
CGContextSaveGState(context)
|
108
|
+
CGContextTranslateCTM(context, 0, 0)
|
109
|
+
color.setFill
|
110
|
+
path.fill
|
111
|
+
CGContextRestoreGState(context)
|
112
|
+
|
113
|
+
big_oval_width = slider_height - 4
|
114
|
+
big_oval = [[-big_oval_width / 2, (slider_height - big_oval_width) / 2], [big_oval_width, big_oval_width]]
|
115
|
+
big_oval_path = UIBezierPath.bezierPathWithOvalInRect(big_oval)
|
116
|
+
small_oval_width = 4
|
117
|
+
small_oval = [[-small_oval_width / 2, (slider_height - small_oval_width) / 2], [small_oval_width, small_oval_width]]
|
118
|
+
small_oval_path = UIBezierPath.bezierPathWithOvalInRect(small_oval)
|
119
|
+
|
120
|
+
points = [0, 1].to_pointer(:float)
|
121
|
+
|
122
|
+
cgcolors_red = [
|
123
|
+
UIColor.colorWithRed(0, green:g, blue:b, alpha:1).CGColor,
|
124
|
+
UIColor.colorWithRed(1, green:g, blue:b, alpha:1).CGColor,
|
125
|
+
]
|
126
|
+
gradient_red = CGGradientCreateWithColors(color_space, cgcolors_red, points)
|
127
|
+
CGContextSaveGState(context)
|
128
|
+
CGContextTranslateCTM(context, 0, 1 * slider_height)
|
129
|
+
path.addClip
|
130
|
+
CGContextDrawLinearGradient(context, gradient_red, self.bounds.top_left, self.bounds.top_right, 0)
|
131
|
+
CGContextTranslateCTM(context, r * bounds.width, 0)
|
132
|
+
UIColor.colorWithRed(1, green:g, blue:b, alpha:1).invert.setStroke
|
133
|
+
big_oval_path.stroke
|
134
|
+
small_oval_path.stroke
|
135
|
+
CGContextRestoreGState(context)
|
136
|
+
|
137
|
+
cgcolors_green = [
|
138
|
+
UIColor.colorWithRed(r, green:0, blue:b, alpha:1).CGColor,
|
139
|
+
UIColor.colorWithRed(r, green:1, blue:b, alpha:1).CGColor,
|
140
|
+
]
|
141
|
+
gradient_green = CGGradientCreateWithColors(color_space, cgcolors_green, points)
|
142
|
+
CGContextSaveGState(context)
|
143
|
+
CGContextTranslateCTM(context, 0, 2 * slider_height)
|
144
|
+
path.addClip
|
145
|
+
CGContextDrawLinearGradient(context, gradient_green, self.bounds.top_left, self.bounds.top_right, 0)
|
146
|
+
CGContextTranslateCTM(context, g * bounds.width, 0)
|
147
|
+
UIColor.colorWithRed(r, green:1, blue:b, alpha:1).invert.setStroke
|
148
|
+
big_oval_path.stroke
|
149
|
+
small_oval_path.stroke
|
150
|
+
CGContextRestoreGState(context)
|
151
|
+
|
152
|
+
cgcolors_blue = [
|
153
|
+
UIColor.colorWithRed(r, green:g, blue:0, alpha:1).CGColor,
|
154
|
+
UIColor.colorWithRed(r, green:g, blue:1, alpha:1).CGColor,
|
155
|
+
]
|
156
|
+
gradient_blue = CGGradientCreateWithColors(color_space, cgcolors_blue, points)
|
157
|
+
CGContextSaveGState(context)
|
158
|
+
CGContextTranslateCTM(context, 0, 3 * slider_height)
|
159
|
+
path.addClip
|
160
|
+
CGContextDrawLinearGradient(context, gradient_blue, self.bounds.top_left, self.bounds.top_right, 0)
|
161
|
+
CGContextTranslateCTM(context, b * bounds.width, 0)
|
162
|
+
UIColor.colorWithRed(r, green:g, blue:1, alpha:1).invert.setStroke
|
163
|
+
big_oval_path.stroke
|
164
|
+
small_oval_path.stroke
|
165
|
+
CGContextRestoreGState(context)
|
166
|
+
|
167
|
+
cgcolors_alpha = [
|
168
|
+
UIColor.colorWithRed(r, green:g, blue:b, alpha:0).CGColor,
|
169
|
+
UIColor.colorWithRed(r, green:g, blue:b, alpha:1).CGColor,
|
170
|
+
]
|
171
|
+
gradient_alpha = CGGradientCreateWithColors(color_space, cgcolors_alpha, points)
|
172
|
+
CGContextSaveGState(context)
|
173
|
+
CGContextTranslateCTM(context, 0, 4 * slider_height)
|
174
|
+
path.addClip
|
175
|
+
CGContextDrawLinearGradient(context, gradient_alpha, self.bounds.top_left, self.bounds.top_right, 0)
|
176
|
+
CGContextTranslateCTM(context, a * bounds.width, 0)
|
177
|
+
:white.uicolor.mix_with(color.uicolor(1).invert, a).setStroke
|
178
|
+
big_oval_path.stroke
|
179
|
+
small_oval_path.stroke
|
180
|
+
CGContextRestoreGState(context)
|
181
|
+
end
|
182
|
+
|
183
|
+
def touchesBegan(touches, withEvent:event)
|
184
|
+
point = touches.anyObject.locationInView(self)
|
185
|
+
@touched_section = nil
|
186
|
+
touched_color_at(point)
|
187
|
+
end
|
188
|
+
|
189
|
+
def touchesMoved(touches, withEvent:event)
|
190
|
+
point = touches.anyObject.locationInView(self)
|
191
|
+
touched_color_at(point)
|
192
|
+
end
|
193
|
+
|
194
|
+
ColorSection = 0
|
195
|
+
RedSection = 1
|
196
|
+
GreenSection = 2
|
197
|
+
BlueSection = 3
|
198
|
+
AlphaSection = 4
|
199
|
+
|
200
|
+
def touched_color_at(point)
|
201
|
+
slider_height = bounds.height / 5
|
202
|
+
section = (point.y / slider_height).floor
|
203
|
+
amount = [[point.x / bounds.width, 0].max, 1].min
|
204
|
+
|
205
|
+
# assigns @touched_section only the first time (in `touchesBegan()`)
|
206
|
+
@touched_section ||= section
|
207
|
+
# makes sure we're still touching the same section
|
208
|
+
section = @touched_section
|
209
|
+
return if section == ColorSection
|
210
|
+
|
211
|
+
r = color.red
|
212
|
+
g = color.green
|
213
|
+
b = color.blue
|
214
|
+
a = color.alpha
|
215
|
+
|
216
|
+
case section
|
217
|
+
when RedSection
|
218
|
+
r = amount
|
219
|
+
when GreenSection
|
220
|
+
g = amount
|
221
|
+
when BlueSection
|
222
|
+
b = amount
|
223
|
+
when AlphaSection
|
224
|
+
amount = (amount * 100).round / 100.0
|
225
|
+
a = amount
|
226
|
+
end
|
227
|
+
|
228
|
+
self.color = UIColor.colorWithRed(r, green:g, blue:b, alpha:a)
|
229
|
+
self.sendActionsForControlEvents(:value_changed.uicontrolevent)
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
end end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Motion ; module Xray
|
2
|
+
|
3
|
+
class XrayDpad < UIView
|
4
|
+
|
5
|
+
def initWithFrame(frame)
|
6
|
+
super.tap do
|
7
|
+
@pressed = {
|
8
|
+
up: 'xray_dpad_up'.uiimage,
|
9
|
+
down: 'xray_dpad_down'.uiimage,
|
10
|
+
left: 'xray_dpad_left'.uiimage,
|
11
|
+
right: 'xray_dpad_right'.uiimage,
|
12
|
+
center: 'xray_dpad_center'.uiimage,
|
13
|
+
}
|
14
|
+
@default = 'xray_dpad'.uiimage
|
15
|
+
@image_view = 'xray_dpad'.uiimageview
|
16
|
+
@pressing = nil
|
17
|
+
self << @image_view
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_pressing_image(direction)
|
22
|
+
image = @pressed[direction] || @default
|
23
|
+
@image_view.image = image
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_pressing(point)
|
27
|
+
center = CGRect.new([24.5, 24.5], [23.0, 23.0])
|
28
|
+
pressing = nil
|
29
|
+
if not self.bounds.contains? point
|
30
|
+
return nil
|
31
|
+
elsif center.contains? point
|
32
|
+
return :center
|
33
|
+
else
|
34
|
+
if point.x < point.y
|
35
|
+
if point.x < self.frame.height - point.y
|
36
|
+
return :left
|
37
|
+
else
|
38
|
+
return :down
|
39
|
+
end
|
40
|
+
else
|
41
|
+
if point.x < self.frame.height - point.y
|
42
|
+
return :up
|
43
|
+
else
|
44
|
+
return :right
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def targets
|
51
|
+
@targets ||= []
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_listener(target, action)
|
55
|
+
targets << [target, action]
|
56
|
+
end
|
57
|
+
|
58
|
+
def fire(delta)
|
59
|
+
if delta.is_a?(Symbol)
|
60
|
+
dx = 0
|
61
|
+
dy = 0
|
62
|
+
case delta
|
63
|
+
when :up
|
64
|
+
dy = -1
|
65
|
+
when :down
|
66
|
+
dy = 1
|
67
|
+
when :left
|
68
|
+
dx = -1
|
69
|
+
when :right
|
70
|
+
dx = 1
|
71
|
+
when :center
|
72
|
+
# pass
|
73
|
+
else
|
74
|
+
raise "huh? #{delta.inspect}"
|
75
|
+
end
|
76
|
+
delta = CGPoint.new(dx, dy)
|
77
|
+
end
|
78
|
+
|
79
|
+
targets.each { |target, action| target.send(action, delta) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def touchesBegan(touches, withEvent:event)
|
83
|
+
super
|
84
|
+
point = touches.anyObject.locationInView(self)
|
85
|
+
|
86
|
+
@pressing = get_pressing(point)
|
87
|
+
@was_pressing = true
|
88
|
+
@started_location = point
|
89
|
+
if @pressing != :center
|
90
|
+
@started_time = NSDate.new.to_i
|
91
|
+
@started_timer = 0.1.every do
|
92
|
+
new_time = NSDate.new.to_i
|
93
|
+
delta = new_time - @started_time
|
94
|
+
break if delta < 0.5
|
95
|
+
|
96
|
+
if @was_pressing
|
97
|
+
fire(@pressing)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
fire(@pressing)
|
101
|
+
end
|
102
|
+
set_pressing_image(@pressing)
|
103
|
+
end
|
104
|
+
|
105
|
+
def touchesMoved(touches, withEvent:event)
|
106
|
+
super
|
107
|
+
return unless @pressing
|
108
|
+
|
109
|
+
point = touches.anyObject.locationInView(self)
|
110
|
+
if @pressing == :center
|
111
|
+
dx = point.x - @started_location.x
|
112
|
+
dy = point.y - @started_location.y
|
113
|
+
fire(CGPoint.new(dx, dy))
|
114
|
+
@started_location = point
|
115
|
+
else
|
116
|
+
if get_pressing(point) != @pressing
|
117
|
+
@was_pressing = false
|
118
|
+
set_pressing_image(nil)
|
119
|
+
else
|
120
|
+
@was_pressing = true
|
121
|
+
set_pressing_image(@pressing)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def touchesEnded(touches, withEvent:event)
|
127
|
+
super
|
128
|
+
@started_timer.invalidate if @started_timer
|
129
|
+
set_pressing_image(nil)
|
130
|
+
@started_timer = nil
|
131
|
+
end
|
132
|
+
|
133
|
+
def touchesCancelled(touches, withEvent:event)
|
134
|
+
super
|
135
|
+
@started_timer.invalidate if @started_timer
|
136
|
+
set_pressing_image(nil)
|
137
|
+
@started_timer = nil
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end end
|