motion-xray 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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