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