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
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+ $:.unshift("/Library/RubyMotion/lib")
3
+ require 'motion/project'
4
+ require './lib/motion-xray'
5
+
6
+
7
+ Motion::Project::App.setup do |app|
8
+ # Use `rake config' to see complete project settings.
9
+ app.name = 'motion-xray'
10
+ app.detect_dependencies = false
11
+ end
@@ -0,0 +1,44 @@
1
+ include SugarCube::Adjust
2
+
3
+
4
+ class AppDelegate
5
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
6
+ @window = Motion::Xray::XrayWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
7
+ ctlr = MyController.new
8
+ first = UINavigationController.alloc.initWithRootViewController(ctlr)
9
+ @window.rootViewController = first
10
+ @window.makeKeyAndVisible
11
+
12
+ Motion::Xray.register(Motion::Xray::SaveUIPlugin.new)
13
+
14
+ true
15
+ end
16
+ end
17
+
18
+
19
+ class MyController < UIViewController
20
+
21
+ def viewDidLoad
22
+ self.view.backgroundColor = :black.uicolor
23
+
24
+ bluebox = UIView.alloc.initWithFrame([[20, 20], [30, 30]])
25
+ bluebox.backgroundColor = :blue.uicolor
26
+ self.view << bluebox
27
+
28
+ darkbox = UIView.alloc.initWithFrame(bluebox.frame.below(8))
29
+ darkbox.backgroundColor = '#222222'.uicolor
30
+ self.view << darkbox
31
+
32
+ lightbox = UIView.alloc.initWithFrame(darkbox.frame.below(8))
33
+ lightbox.backgroundColor = :white.uicolor
34
+ self.view << lightbox
35
+
36
+ label = UILabel.alloc.initWithFrame(lightbox.frame.below(8))
37
+ label.backgroundColor = :clear.uicolor
38
+ label.textColor = :white.uicolor
39
+ label.text = 'A Label'
40
+ label.sizeToFit
41
+ self.view << label
42
+ end
43
+
44
+ end
@@ -0,0 +1,37 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "The motion-xray gem must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+ require 'sugarcube'
6
+ require 'geomotion'
7
+
8
+ Motion::Project::App.setup do |app|
9
+ # scans app.files until it finds app/ (the default)
10
+ # if found, it inserts just before those files, otherwise it will insert to
11
+ # the end of the list
12
+ insert_point = 0
13
+ app.files.each_index do |index|
14
+ file = app.files[index]
15
+ if file =~ /^(?:\.\/)?app\//
16
+ # found app/, so stop looking
17
+ break
18
+ end
19
+ insert_point = index + 1
20
+ end
21
+
22
+ Dir.glob(File.join(File.dirname(__FILE__), 'motion-xray/z_editors/*.rb')).reverse.each do |file|
23
+ app.files.insert(insert_point, file)
24
+ end
25
+ Dir.glob(File.join(File.dirname(__FILE__), 'motion-xray/views/*.rb')).reverse.each do |file|
26
+ app.files.insert(insert_point, file)
27
+ end
28
+ Dir.glob(File.join(File.dirname(__FILE__), 'motion-xray/plugins/*.rb')).reverse.each do |file|
29
+ app.files.insert(insert_point, file)
30
+ end
31
+ Dir.glob(File.join(File.dirname(__FILE__), 'motion-xray/*.rb')).reverse.each do |file|
32
+ app.files.insert(insert_point, file)
33
+ end
34
+
35
+ app.resources_dirs << File.join(File.dirname(__FILE__), 'resources')
36
+ app.frameworks << 'MessageUI'
37
+ end
@@ -0,0 +1,129 @@
1
+ module Motion ; module Xray
2
+
3
+ class AccessibilityPlugin < Plugin
4
+ name 'Accessibility'
5
+
6
+ def plugin_view(canvas)
7
+ return UIView.alloc.initWithFrame(canvas.bounds).tap do |view|
8
+ view.backgroundColor = :black.uicolor
9
+
10
+ @colorblind = UIButton.alloc.initWithFrame(view.bounds
11
+ .thinner(view.bounds.width / 2)
12
+ .right(view.bounds.width / 2))
13
+ @accessibility = UIButton.alloc.initWithFrame(view.bounds
14
+ .thinner(view.bounds.width / 2))
15
+
16
+ @big_image = UIButton.alloc.initWithFrame(Xray.window.bounds)
17
+ @big_image.backgroundColor = :black.uicolor
18
+ @big_image.on :touch {
19
+ @big_image.fade_out_and_remove
20
+ }
21
+
22
+ @colorblind.on :touch {
23
+ show_big_colorblind(@colorblind.imageForState(:normal.uicontrolstate))
24
+ }
25
+
26
+ @accessibility.on :touch {
27
+ show_big_colorblind(@accessibility.imageForState(:normal.uicontrolstate))
28
+ }
29
+
30
+ @colorblind_spinner = UIActivityIndicatorView.white
31
+ @colorblind_spinner.center = @colorblind.center
32
+ @colorblind_spinner.hidesWhenStopped
33
+ @colorblind_spinner.stopAnimating
34
+
35
+ @accessibility_spinner = UIActivityIndicatorView.white
36
+ @accessibility_spinner.center = @accessibility.center
37
+ @accessibility_spinner.hidesWhenStopped
38
+ @accessibility_spinner.stopAnimating
39
+
40
+ view << @colorblind_spinner
41
+ view << @accessibility_spinner
42
+ view << @colorblind
43
+ view << @accessibility
44
+ end
45
+ end
46
+
47
+ def show
48
+ @colorblind.setImage(nil, forState: :normal.uicontrolstate)
49
+ @accessibility.setImage(nil, forState: :normal.uicontrolstate)
50
+ @colorblind_spinner.startAnimating
51
+ @accessibility_spinner.startAnimating
52
+
53
+ Dispatch::Queue.concurrent(:default).async do
54
+ image = get_colorblind_image
55
+ Dispatch::Queue.main.async do
56
+ @colorblind.setImage(image, forState: :normal.uicontrolstate)
57
+ @colorblind_spinner.stopAnimating
58
+ end
59
+ end
60
+ Dispatch::Queue.concurrent(:default).async do
61
+ image = get_accessibility_image
62
+ Dispatch::Queue.main.async do
63
+ @accessibility.setImage(image, forState: :normal.uicontrolstate)
64
+ @accessibility_spinner.stopAnimating
65
+ end
66
+ end
67
+ end
68
+
69
+ def get_colorblind_image
70
+ Xray.ui.get_screenshot.darken(brightness:-0.1, saturation:0)
71
+ end
72
+
73
+ def get_accessibility_image
74
+ views = Xray.ui.collect_visible_views.map {|view|
75
+ # if the view "is accessible", draw a green square
76
+ # otherwise a red one
77
+ f = view.convertRect(view.bounds, toView:nil)
78
+ f.origin.x *= 2
79
+ f.origin.y *= 2
80
+ f.size.width *= 2
81
+ f.size.height *= 2
82
+ retval = UIView.alloc.initWithFrame(f)
83
+
84
+ if is_accessible?(view)
85
+ retval.backgroundColor = good_color
86
+ else
87
+ retval.backgroundColor = bad_color
88
+ end
89
+ retval
90
+ }
91
+
92
+ scale = UIScreen.mainScreen.scale
93
+ UIGraphicsBeginImageContextWithOptions(Xray.window.bounds.size, false, scale)
94
+ context = UIGraphicsGetCurrentContext()
95
+
96
+ views.reverse.each do |subview|
97
+ CGContextSaveGState(context)
98
+ CGContextTranslateCTM(context, subview.frame.origin.x, subview.frame.origin.y)
99
+ subview.layer.renderInContext(context)
100
+ CGContextRestoreGState(context)
101
+ end
102
+ image = UIGraphicsGetImageFromCurrentImageContext()
103
+ UIGraphicsEndImageContext()
104
+ return image
105
+ end
106
+
107
+ def is_accessible?(view)
108
+ !!view.accessibilityLabel
109
+ end
110
+
111
+ def good_color
112
+ :green.uicolor(0.5)
113
+ end
114
+
115
+ def bad_color
116
+ :red.uicolor(0.1)
117
+ end
118
+
119
+ def show_big_colorblind(image)
120
+ @big_image.setImage(image, forState: :normal.uicontrolstate)
121
+ @big_image.alpha = 0
122
+ Xray.window << @big_image
123
+ @big_image.fade_in {
124
+ }
125
+ end
126
+
127
+ end
128
+
129
+ end end
@@ -0,0 +1,301 @@
1
+ module Motion ; module Xray
2
+
3
+ class LogPlugin < Plugin
4
+ LogChangedNotification = 'Motion::Xray::LogPlugin::LogChangedNotification'
5
+
6
+ class << self
7
+ def log
8
+ @log || clear!
9
+ end
10
+
11
+ def clear!
12
+ @log = []
13
+ end
14
+
15
+ def add_log(type, args)
16
+ message = sprintf(*args)
17
+ case type
18
+ when :error, :red
19
+ nslog = "\033[31;4;1mERROR!\033[0m\033[31m #{message}\033[0m"
20
+ log_message = NSMutableAttributedString.alloc.initWithString("ERROR! #{message}\n", attributes: {
21
+ NSForegroundColorAttributeName => 0xCB7172.uicolor,
22
+ })
23
+ when :log, :white
24
+ nslog = "\033[37m#{message}\033[0m"
25
+ log_message = NSMutableAttributedString.alloc.initWithString("#{message}\n", attributes: {
26
+ NSForegroundColorAttributeName => 0xBCBEAB.uicolor,
27
+ })
28
+ when :warning, :yellow
29
+ nslog = "\033[33m#{message}\033[0m"
30
+ log_message = NSMutableAttributedString.alloc.initWithString("#{message}\n", attributes: {
31
+ NSForegroundColorAttributeName => 0xDFAF8F.uicolor,
32
+ })
33
+ when :notice, :magenta
34
+ nslog = "\033[35m#{message}\033[0m"
35
+ log_message = NSMutableAttributedString.alloc.initWithString("#{message}\n", attributes: {
36
+ NSForegroundColorAttributeName => 0xBB65A1.uicolor,
37
+ })
38
+ when :info, :cyan
39
+ nslog = "\033[36m#{message}\033[0m"
40
+ log_message = NSMutableAttributedString.alloc.initWithString("#{message}\n", attributes: {
41
+ NSForegroundColorAttributeName => 0x67AAAD.uicolor,
42
+ })
43
+ when :ok, :green
44
+ nslog = "\033[32m#{message}\033[0m"
45
+ log_message = NSMutableAttributedString.alloc.initWithString("#{message}\n", attributes: {
46
+ NSForegroundColorAttributeName => 0x60B48A.uicolor,
47
+ })
48
+ when :debug, :blue
49
+ nslog = "\033[34m#{message}\033[0m"
50
+ log_message = NSMutableAttributedString.alloc.initWithString("#{message}\n", attributes: {
51
+ NSForegroundColorAttributeName => 0x6B8197.uicolor,
52
+ })
53
+ else
54
+ raise "huh?"
55
+ end
56
+
57
+ if Log.level >= Log::Levels[type]
58
+ NSLog(nslog)
59
+ end
60
+
61
+ entry = {message:log_message, date:NSDate.new}
62
+ log << entry
63
+ LogChangedNotification.post_notification(nil, entry)
64
+
65
+ return nil
66
+ end
67
+ end
68
+
69
+ name 'Logs'
70
+ ActionsWidth = 25
71
+
72
+ def plugin_view(canvas)
73
+ @text_view = UITextView.alloc.initWithFrame(canvas.bounds).tap do |text_view|
74
+ text_view.editable = false
75
+ text_view.delegate = self
76
+ text_view.backgroundColor = 0x2b2b2b.uicolor
77
+ end
78
+
79
+ @toggle_datetimes_button = UIButton.alloc.initWithFrame([[0, 0], [7, 259]])
80
+ @toggle_datetimes_button.setImage('xray_drawer_right'.uiimage, forState: :normal.uicontrolstate)
81
+ @toggle_datetimes_button.on :touch {
82
+ toggle_datetimes
83
+ }
84
+
85
+ @toggle_actions_button = UIButton.alloc.initWithFrame([[0, 0], [7, 259]])
86
+ @toggle_actions_button.setImage('xray_drawer_left'.uiimage, forState: :normal.uicontrolstate)
87
+ @toggle_actions_button.on :touch {
88
+ toggle_actions
89
+ }
90
+
91
+ btn_width = @toggle_actions_button.frame.width
92
+ actions_frame = [[canvas.bounds.width - btn_width, 0], [ActionsWidth + btn_width, 259]]
93
+ @actions_container = UIView.alloc.initWithFrame(actions_frame).tap do |actions_container|
94
+ button_y = 0
95
+ clear_button = UIButton.custom
96
+ clear_button.setImage('xray_clear_button'.uiimage, forState: :normal.uicontrolstate)
97
+ clear_button.frame = [[@toggle_actions_button.frame.width, button_y], [ActionsWidth, ActionsWidth]]
98
+ clear_button.on :touch {
99
+ LogPlugin.clear!
100
+ update_log
101
+ }
102
+ actions_container << clear_button
103
+ button_y += ActionsWidth
104
+
105
+ # send email button
106
+ if MFMailComposeViewController.canSendMail
107
+ email_button = UIButton.custom
108
+ email_button.setImage('xray_email_button'.uiimage, forState: :normal.uicontrolstate)
109
+ email_button.frame = [[@toggle_actions_button.frame.width, button_y], [ActionsWidth, ActionsWidth]]
110
+ email_button.on :touch {
111
+ mail_view_controller = MFMailComposeViewController.new
112
+ mail_view_controller.mailComposeDelegate = self
113
+ mail_view_controller.setSubject('From Motion-Xray.')
114
+ mail_view_controller.setMessageBody(
115
+ '<pre>' << LogPlugin.log.map{ |line|
116
+ line[:date].string_with_format('HH:mm:ss.SSS') << ' ' << line[:message].string
117
+ }.join("\n") << '</pre>', isHTML: true)
118
+ SugarCube::Modal.present_modal mail_view_controller
119
+ }
120
+ actions_container << email_button
121
+ button_y += ActionsWidth
122
+ end
123
+ end
124
+ @actions_container << @toggle_actions_button
125
+
126
+ @showing_datetimes = false
127
+ @showing_actions = false
128
+ return UIView.alloc.initWithFrame(canvas.bounds).tap do |view|
129
+ view << @text_view
130
+ view << @toggle_datetimes_button
131
+ view << @actions_container
132
+ view.backgroundColor = 0x2b2b2b.uicolor
133
+ end
134
+ end
135
+
136
+ def edit(target)
137
+ super
138
+ LogPlugin.add_log(:info, target.inspect)
139
+ end
140
+
141
+ def toggle_datetimes
142
+ if @showing_datetimes
143
+ hide_datetimes
144
+ else
145
+ show_datetimes
146
+ end
147
+ end
148
+
149
+ def show_datetimes
150
+ return if @showing_datetimes
151
+
152
+ @showing_datetimes = true
153
+ @toggle_datetimes_button.setImage('xray_drawer_left'.uiimage, forState: :normal.uicontrolstate)
154
+ update_log
155
+ end
156
+
157
+ def hide_datetimes
158
+ return unless @showing_datetimes
159
+
160
+ @showing_datetimes = false
161
+ @toggle_datetimes_button.setImage('xray_drawer_right'.uiimage, forState: :normal.uicontrolstate)
162
+ update_log
163
+ end
164
+
165
+ def toggle_actions
166
+ if @showing_actions
167
+ hide_actions
168
+ else
169
+ show_actions
170
+ end
171
+ end
172
+
173
+ def show_actions
174
+ return if @showing_actions
175
+
176
+ @showing_actions = true
177
+ @toggle_actions_button.setImage('xray_drawer_right'.uiimage, forState: :normal.uicontrolstate)
178
+ @actions_container.slide(:left, size: ActionsWidth, options: UIViewAnimationOptionCurveLinear)
179
+ @text_view.frame = @text_view.frame.thinner(ActionsWidth)
180
+ end
181
+
182
+ def hide_actions
183
+ return unless @showing_actions
184
+
185
+ @showing_actions = false
186
+ @toggle_actions_button.setImage('xray_drawer_left'.uiimage, forState: :normal.uicontrolstate)
187
+ @actions_container.slide(:right, size: ActionsWidth, options: UIViewAnimationOptionCurveLinear)
188
+ @text_view.frame = @text_view.frame.wider(ActionsWidth)
189
+ end
190
+
191
+ def show
192
+ LogChangedNotification.add_observer(self, :'update_log:')
193
+ update_log
194
+ end
195
+
196
+ def hide
197
+ LogChangedNotification.remove_observer(self)
198
+ end
199
+
200
+ def update_log(notification=nil)
201
+ if @text_view
202
+ log = NSMutableAttributedString.alloc.init
203
+ if notification
204
+ log.appendAttributedString(@text_view.attributedText)
205
+ append_entry(log, notification.userInfo)
206
+ else
207
+ LogPlugin.log.each do |msg|
208
+ append_entry(log, msg)
209
+ end
210
+ end
211
+ # this can't be set in LogPlugin.add_log (font is not available during startup)
212
+ log.addAttribute(NSFontAttributeName, value: :monospace.uifont(10), range:NSRange.new(0, log.length))
213
+ @text_view.attributedText = log
214
+ end
215
+ end
216
+
217
+ def append_entry(log, entry)
218
+ if @showing_datetimes
219
+ date = entry[:date].string_with_format('HH:mm:ss.SSS')
220
+ log.appendAttributedString(NSMutableAttributedString.alloc.initWithString("#{date} ", attributes: {
221
+ NSForegroundColorAttributeName => 0xBCBEAB.uicolor,
222
+ }))
223
+ end
224
+ log.appendAttributedString(entry[:message])
225
+ end
226
+
227
+ def mailComposeController(controller, didFinishWithResult:result, error:error)
228
+ SugarCube::Modal.dismiss_modal {
229
+ Xray.fire_up
230
+ }
231
+ end
232
+
233
+ end
234
+
235
+ module Log
236
+ module_function
237
+
238
+ Error = 1
239
+ Log = 2
240
+ Warning = 3
241
+ Notice = 4
242
+ Info = 5
243
+ Ok = 6
244
+ Debug = 7
245
+
246
+ Levels = {
247
+ error: Error,
248
+ log: Log,
249
+ warning: Warning,
250
+ notice: Notice,
251
+ info: Info,
252
+ ok: Ok,
253
+ debug: Debug,
254
+ }
255
+
256
+ def level=(level)
257
+ @level = Levels[level] || level
258
+ end
259
+
260
+ def level
261
+ @level ||= Ok
262
+ end
263
+
264
+ def _log(type, args)
265
+ args = [''] if args == []
266
+ LogPlugin.add_log(type, args)
267
+ end
268
+
269
+ def error(*args)
270
+ _log(:error, args)
271
+ end
272
+
273
+ def log(*args)
274
+ _log(:log, args)
275
+ end
276
+
277
+ def warning(*args)
278
+ _log(:warning, args)
279
+ end
280
+ def warn(*args)
281
+ _log(:warning, args)
282
+ end
283
+
284
+ def notice(*args)
285
+ _log(:notice, args)
286
+ end
287
+
288
+ def info(*args)
289
+ _log(:info, args)
290
+ end
291
+
292
+ def ok(*args)
293
+ _log(:ok, args)
294
+ end
295
+
296
+ def debug(*args)
297
+ _log(:debug, args)
298
+ end
299
+ end
300
+
301
+ end end