motion-xray 1.0.4

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.
Files changed (56) hide show
  1. data/.gitignore +21 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +28 -0
  4. data/README.md +426 -0
  5. data/Rakefile +11 -0
  6. data/app/app_delegate.rb +44 -0
  7. data/lib/motion-xray.rb +37 -0
  8. data/lib/motion-xray/plugins/accessibility_plugin.rb +129 -0
  9. data/lib/motion-xray/plugins/log_plugin.rb +301 -0
  10. data/lib/motion-xray/plugins/save_ui_plugin.rb +142 -0
  11. data/lib/motion-xray/plugins/ui_plugin.rb +41 -0
  12. data/lib/motion-xray/version.rb +5 -0
  13. data/lib/motion-xray/views/xray_color_swatch.rb +234 -0
  14. data/lib/motion-xray/views/xray_dpad.rb +142 -0
  15. data/lib/motion-xray/views/xray_gradient_view.rb +23 -0
  16. data/lib/motion-xray/views/xray_headers.rb +101 -0
  17. data/lib/motion-xray/views/xray_lock_button.rb +50 -0
  18. data/lib/motion-xray/views/xray_scroll_view.rb +12 -0
  19. data/lib/motion-xray/views/xray_toolbar.rb +173 -0
  20. data/lib/motion-xray/views/xray_window.rb +13 -0
  21. data/lib/motion-xray/xray.rb +56 -0
  22. data/lib/motion-xray/xray_constants.rb +5 -0
  23. data/lib/motion-xray/xray_dummy.rb +8 -0
  24. data/lib/motion-xray/xray_editors.rb +62 -0
  25. data/lib/motion-xray/xray_ext.rb +125 -0
  26. data/lib/motion-xray/xray_plugin.rb +40 -0
  27. data/lib/motion-xray/xray_typewriter.rb +217 -0
  28. data/lib/motion-xray/xray_ui.rb +723 -0
  29. data/lib/motion-xray/z_editors/xray_boolean_editor.rb +24 -0
  30. data/lib/motion-xray/z_editors/xray_color_editor.rb +119 -0
  31. data/lib/motion-xray/z_editors/xray_frame_editor.rb +108 -0
  32. data/lib/motion-xray/z_editors/xray_image_editor.rb +78 -0
  33. data/lib/motion-xray/z_editors/xray_text_editor.rb +94 -0
  34. data/lib/resources/xray_button_bg@2x.png +0 -0
  35. data/lib/resources/xray_choose_button@2x.png +0 -0
  36. data/lib/resources/xray_clear_button@2x.png +0 -0
  37. data/lib/resources/xray_detail_button@2x.png +0 -0
  38. data/lib/resources/xray_dpad@2x.png +0 -0
  39. data/lib/resources/xray_dpad_center@2x.png +0 -0
  40. data/lib/resources/xray_dpad_down@2x.png +0 -0
  41. data/lib/resources/xray_dpad_left@2x.png +0 -0
  42. data/lib/resources/xray_dpad_right@2x.png +0 -0
  43. data/lib/resources/xray_dpad_up@2x.png +0 -0
  44. data/lib/resources/xray_drawer_left@2x.png +0 -0
  45. data/lib/resources/xray_drawer_right@2x.png +0 -0
  46. data/lib/resources/xray_edit_button@2x.png +0 -0
  47. data/lib/resources/xray_email_button@2x.png +0 -0
  48. data/lib/resources/xray_lock_button_horizontal@2x.png +0 -0
  49. data/lib/resources/xray_lock_button_locked@2x.png +0 -0
  50. data/lib/resources/xray_lock_button_unlocked@2x.png +0 -0
  51. data/lib/resources/xray_lock_button_vertical@2x.png +0 -0
  52. data/motion-xray.gemspec +40 -0
  53. data/resources/Default-568h@2x.png +0 -0
  54. data/spec/xray_view_spec.rb +43 -0
  55. data/vendor/Podfile.lock +11 -0
  56. 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,5 @@
1
+ module Motion
2
+ module Xray
3
+ Version = '1.0.4'
4
+ end
5
+ 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