ProMotion 0.5.2 → 0.6.0

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 (46) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +6 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +5 -1
  5. data/README.md +237 -138
  6. data/Rakefile +4 -9
  7. data/app/app_delegate.rb +3 -0
  8. data/app/screens/basic_screen.rb +27 -0
  9. data/lib/ProMotion.rb +3 -5
  10. data/lib/ProMotion/cocoatouch/SplitViewController.rb +25 -0
  11. data/lib/ProMotion/cocoatouch/TableViewController.rb +5 -5
  12. data/lib/ProMotion/cocoatouch/ViewController.rb +5 -5
  13. data/lib/ProMotion/{app_delegate.rb → delegate.rb} +23 -23
  14. data/lib/ProMotion/helpers/console.rb +6 -4
  15. data/lib/ProMotion/helpers/logger.rb +73 -0
  16. data/lib/ProMotion/helpers/view_helper.rb +45 -13
  17. data/lib/ProMotion/screen_helpers/_tables/_refreshable_table.rb +42 -0
  18. data/lib/ProMotion/screen_helpers/_tables/_searchable_table.rb +2 -2
  19. data/lib/ProMotion/screen_helpers/_tables/_sectioned_table.rb +46 -41
  20. data/lib/ProMotion/screen_helpers/_tables/grouped_table.rb +2 -1
  21. data/lib/ProMotion/screen_helpers/_tables/plain_table.rb +1 -0
  22. data/lib/ProMotion/screen_helpers/screen_elements.rb +16 -11
  23. data/lib/ProMotion/screen_helpers/screen_navigation.rb +15 -16
  24. data/lib/ProMotion/screen_helpers/screen_tabs.rb +20 -16
  25. data/lib/ProMotion/screen_helpers/split_screen.rb +42 -0
  26. data/lib/ProMotion/screens/_screen_module.rb +44 -35
  27. data/lib/ProMotion/screens/_table_screen_module.rb +18 -1
  28. data/lib/ProMotion/screens/screen.rb +1 -1
  29. data/lib/ProMotion/version.rb +1 -1
  30. data/spec/helpers/table_screen.rb +48 -0
  31. data/spec/helpers/table_screen_refreshable.rb +11 -0
  32. data/spec/helpers/table_screen_searchable.rb +5 -0
  33. data/spec/helpers/test_delegate.rb +9 -0
  34. data/spec/ios_version_spec.rb +6 -6
  35. data/spec/logger_spec.rb +68 -0
  36. data/spec/main_spec.rb +1 -1
  37. data/spec/screen_helpers_spec.rb +35 -6
  38. data/spec/{view_controller_spec.rb → screen_spec.rb} +1 -1
  39. data/spec/split_screen_in_tab_bar_spec.rb +49 -0
  40. data/spec/split_screen_open_screen_spec.rb +46 -0
  41. data/spec/split_screen_spec.rb +35 -0
  42. data/spec/table_screen_spec.rb +72 -0
  43. data/spec/view_helper_spec.rb +112 -8
  44. metadata +29 -8
  45. data/lib/ProMotion/helpers/tab_bar.rb +0 -115
  46. data/spec/helpers/.gitkeep +0 -0
data/Rakefile CHANGED
@@ -3,20 +3,15 @@ require 'motion/project'
3
3
  require 'bundler/gem_tasks'
4
4
  Bundler.setup
5
5
  Bundler.require
6
- # require 'motion-table'
7
6
 
8
7
  Motion::Project::App.setup do |app|
9
- # Use `rake config' to see complete project settings.
10
8
  app.name = 'ProMotionTest'
11
- app.version = "0.3.0"
12
-
9
+ app.version = "0.99.0" # I've got 99 problems and the test app's version isn't one of them
10
+ app.redgreen_style = :focused # :focused, :full
13
11
 
14
12
  # Devices
15
- app.deployment_target = "5.0"
16
- app.device_family = [:iphone, :ipad]
13
+ app.deployment_target = "6.0"
14
+ app.device_family = [:ipad] # so we can test split screen capability
17
15
 
18
16
  app.detect_dependencies = true
19
-
20
- # Preload screens
21
- # app.files = Dir.glob(File.join(app.project_dir, 'lib/**/*.rb')) | app.files
22
17
  end
data/app/app_delegate.rb CHANGED
@@ -1,2 +1,5 @@
1
1
  class AppDelegate
2
+ def on_load(app, options)
3
+ open BasicScreen
4
+ end
2
5
  end
@@ -0,0 +1,27 @@
1
+ class BasicScreen < PM::Screen
2
+ title "Basic"
3
+
4
+ def on_init
5
+ # Fires right after the screen is initialized
6
+ end
7
+
8
+ def on_load
9
+ # Fires just before a screen is opened for the first time.
10
+ end
11
+
12
+ def will_appear
13
+ # Fires every time the screen will appear
14
+ end
15
+
16
+ def on_appear
17
+ # Fires just after the screen appears somewhere (after animations are complete)
18
+ end
19
+
20
+ def will_disappear
21
+ # Fires just before the screen will disappear
22
+ end
23
+
24
+ def on_disappear
25
+ # Fires after the screen is fully hidden
26
+ end
27
+ end
data/lib/ProMotion.rb CHANGED
@@ -5,10 +5,8 @@ end
5
5
  require "ProMotion/version"
6
6
 
7
7
  Motion::Project::App.setup do |app|
8
- # app_delegate = Dir.glob(File.join(File.dirname(__FILE__), 'ProMotion/app_delegate.rb'))
9
- # app.files = Dir.glob(File.join(File.dirname(__FILE__), 'ProMotion/**/*.rb')) | app_delegate | app.files
10
-
11
8
  original_files = app.files
12
- app.files = FileList[File.join(File.dirname(__FILE__), 'ProMotion/**/*.rb')].exclude(File.join(File.dirname(__FILE__), 'ProMotion/app_delegate.rb'))
13
- app.files = app.files | Dir.glob(File.join(File.dirname(__FILE__), 'ProMotion/app_delegate.rb')) | original_files
9
+ delegate = File.join(File.dirname(__FILE__), 'ProMotion/delegate.rb')
10
+ promotion_files = FileList[File.join(File.dirname(__FILE__), 'ProMotion/**/*.rb')].exclude(delegate).to_a
11
+ app.files = (promotion_files << delegate) + original_files
14
12
  end
@@ -0,0 +1,25 @@
1
+ class SplitViewController < UISplitViewController
2
+ def master_screen
3
+ s = self.viewControllers.first
4
+ s.respond_to?(:visibleViewController) ? s.visibleViewController : s
5
+ end
6
+ def detail_screen
7
+ s = self.viewControllers.last
8
+ s.respond_to?(:visibleViewController) ? s.visibleViewController : s
9
+ end
10
+ def master_screen=(s)
11
+ self.viewControllers = [s.main_controller, self.viewControllers.last]
12
+ end
13
+ def detail_screen=(s)
14
+ # set the button from the old detail screen to the new one
15
+ button = detail_screen.navigationItem.leftBarButtonItem
16
+ s.navigationItem.leftBarButtonItem = button
17
+
18
+ vc = s.respond_to?(:main_controller) ? s.main_controller : s
19
+
20
+ self.viewControllers = [self.viewControllers.first, vc]
21
+ end
22
+ def screens=(s_array)
23
+ self.viewControllers = s_array
24
+ end
25
+ end
@@ -20,15 +20,15 @@ module ProMotion
20
20
  super
21
21
  self.view_did_appear(animated) if self.respond_to?("view_did_appear:")
22
22
  end
23
-
23
+
24
24
  def viewWillDisappear(animated)
25
25
  self.view_will_disappear(animated) if self.respond_to?("view_will_disappear:")
26
- super
26
+ super
27
27
  end
28
-
28
+
29
29
  def viewDidDisappear(animated)
30
30
  self.view_did_disappear(animated) if self.respond_to?("view_did_disappear:")
31
- super
31
+ super
32
32
  end
33
33
 
34
34
  def shouldAutorotateToInterfaceOrientation(orientation)
@@ -42,7 +42,7 @@ module ProMotion
42
42
  def willRotateToInterfaceOrientation(orientation, duration:duration)
43
43
  self.will_rotate(orientation, duration)
44
44
  end
45
-
45
+
46
46
  def didRotateFromInterfaceOrientation(orientation)
47
47
  self.on_rotate
48
48
  end
@@ -20,17 +20,17 @@ module ProMotion
20
20
  super
21
21
  self.view_did_appear(animated) if self.respond_to?("view_did_appear:")
22
22
  end
23
-
23
+
24
24
  def viewWillDisappear(animated)
25
25
  self.view_will_disappear(animated) if self.respond_to?("view_will_disappear:")
26
- super
26
+ super
27
27
  end
28
-
28
+
29
29
  def viewDidDisappear(animated)
30
30
  if self.respond_to?("view_did_disappear:")
31
31
  self.view_did_disappear(animated)
32
32
  end
33
- super
33
+ super
34
34
  end
35
35
 
36
36
  def shouldAutorotateToInterfaceOrientation(orientation)
@@ -44,7 +44,7 @@ module ProMotion
44
44
  def willRotateToInterfaceOrientation(orientation, duration:duration)
45
45
  self.will_rotate(orientation, duration)
46
46
  end
47
-
47
+
48
48
  def didRotateFromInterfaceOrientation(orientation)
49
49
  self.on_rotate
50
50
  end
@@ -1,20 +1,19 @@
1
1
  module ProMotion
2
- class AppDelegateParent
2
+ class Delegate
3
3
  include ProMotion::ScreenTabs
4
+ include ProMotion::SplitScreen if NSBundle.mainBundle.infoDictionary["UIDeviceFamily"].include?("2") # Only with iPad
4
5
  attr_accessor :window
5
-
6
- def application(application, didFinishLaunchingWithOptions:launch_options)
7
- return true if RUBYMOTION_ENV == "test"
8
6
 
9
- Console.log(" Your AppDelegate (usually in app_delegate.rb) needs an on_load(options) method.", with_color: Console::RED_COLOR) unless self.respond_to?("on_load:")
7
+ def application(application, didFinishLaunchingWithOptions:launch_options)
8
+ unless self.respond_to?(:on_load)
9
+ PM.logger.error "Your AppDelegate (usually in app_delegate.rb) needs an on_load(application, options) method."
10
+ end
10
11
 
11
12
  on_load(application, launch_options)
12
13
 
13
- open_home_screen if has_home_screen
14
-
15
14
  true
16
15
  end
17
-
16
+
18
17
  def app_delegate
19
18
  UIApplication.sharedApplication.delegate
20
19
  end
@@ -23,35 +22,35 @@ module ProMotion
23
22
  self.app_delegate.window
24
23
  end
25
24
 
25
+ def home(screen)
26
+ screen = screen.new if screen.respond_to?(:new)
27
+ @home_screen = screen
28
+ end
29
+
26
30
  def load_root_screen(new_screen)
27
31
  new_screen = new_screen.main_controller if new_screen.respond_to?(:main_controller)
28
32
 
29
- self.window ||= UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
33
+ self.window ||= self.ui_window.alloc.initWithFrame(UIScreen.mainScreen.bounds)
30
34
  self.window.rootViewController = new_screen
31
35
  self.window.makeKeyAndVisible
32
36
  end
33
-
34
- def open_screen(screen)
35
- home(screen)
36
- end
37
- alias :open :open_screen
38
-
39
- def home(screen)
40
- screen = screen.new if screen.respond_to?(:new)
41
- @home_screen = screen
37
+
38
+ def ui_window
39
+ (defined?(Motion) && defined?(Motion::Xray) && defined?(Motion::Xray::XrayWindow)) ? Motion::Xray::XrayWindow : UIWindow
42
40
  end
43
41
 
44
- def open_root_screen(new_screen)
45
- home(new_screen)
42
+ def open_screen(screen, args={})
43
+ home(screen)
46
44
  open_home_screen
47
45
  end
48
- alias :fresh_start :open_root_screen
46
+ alias :open :open_screen
47
+ alias :open_root_screen :open_screen
49
48
 
50
49
  def open_home_screen
51
50
  get_home_screen.send(:on_load) if get_home_screen.respond_to?(:on_load)
52
51
  load_root_screen get_home_screen
53
52
  end
54
-
53
+
55
54
  def get_home_screen
56
55
  @home_screen
57
56
  end
@@ -60,4 +59,5 @@ module ProMotion
60
59
  @home_screen.nil? == false
61
60
  end
62
61
  end
63
- end
62
+ class AppDelegateParent < Delegate; end # For backwards compatibility
63
+ end
@@ -3,23 +3,25 @@ module ProMotion
3
3
  NAME = "RubyMotion::Console: "
4
4
  DEFAULT_COLOR = [ '', '' ]
5
5
  RED_COLOR = [ "\e[0;31m", "\e[0m" ] # Must be in double quotes
6
- GREEN_COLOR = [ "\e[0;32m", "\e[0m" ]
7
- PURPLE_COLOR = [ "\e[0;35m", "\e[0m" ]
6
+ GREEN_COLOR = [ "\e[0;32m", "\e[0m" ]
7
+ PURPLE_COLOR = [ "\e[0;35m", "\e[0m" ]
8
8
 
9
9
  class << self
10
10
  def log(log, with_color:color)
11
11
  return if RUBYMOTION_ENV == "test"
12
+ PM.logger.deprecated "ProMotion::Console.log is deprecated. Use PM.logger (see README)"
12
13
  puts color[0] + NAME + log + color[1]
13
14
  end
14
-
15
+
15
16
  def log(log, withColor:color)
16
17
  return if RUBYMOTION_ENV == "test"
17
- warn "[DEPRECATION] `log(log, withColor:color)` is deprecated. Use `log(log, with_color:color)`"
18
+ PM.logger.deprecated "ProMotion::Console.log is deprecated. Use PM.logger (see README)"
18
19
  self.log(log, with_color:color)
19
20
  end
20
21
 
21
22
  def log(log)
22
23
  return if RUBYMOTION_ENV == "test"
24
+ PM.logger.deprecated "ProMotion::Console.log is deprecated. Use PM.logger (see README)"
23
25
  log(log, with_color: DEFAULT_COLOR)
24
26
  end
25
27
  end
@@ -0,0 +1,73 @@
1
+ module ProMotion
2
+ class Logger
3
+ attr_accessor :level
4
+
5
+ NAME = "ProMotion::Logger: "
6
+
7
+ COLORS = {
8
+ default: [ '', '' ],
9
+ red: [ "\e[0;31m", "\e[0m" ],
10
+ green: [ "\e[0;32m", "\e[0m" ],
11
+ yellow: [ "\e[0;33m", "\e[0m" ],
12
+ blue: [ "\e[0;34m", "\e[0m" ],
13
+ purple: [ "\e[0;35m", "\e[0m" ],
14
+ cyan: [ "\e[0;36m", "\e[0m" ]
15
+ }
16
+
17
+ LEVELS = {
18
+ off: [],
19
+ error: [:error],
20
+ warn: [:error, :warn],
21
+ info: [:error, :warn, :info],
22
+ verbose: [:error, :warn, :info, :debug, :verbose],
23
+ debug: [:error, :warn, :info, :debug, :verbose]
24
+ }
25
+
26
+ def level
27
+ @level ||= :debug
28
+ end
29
+
30
+ def levels
31
+ LEVELS[self.level] || []
32
+ end
33
+
34
+ # Usage: PM.logger.log("ERROR", "message here", :red)
35
+ def log(label, message_text, color)
36
+ return if RUBYMOTION_ENV == "test"
37
+ color = COLORS[color] || COLORS[:default]
38
+ puts color[0] + NAME + "[#{label}] #{message_text}" + color[1]
39
+ end
40
+
41
+ def error(message)
42
+ self.log('ERROR', message, :red) if self.levels.include?(:error)
43
+ end
44
+
45
+ def deprecated(message)
46
+ self.log('DEPRECATED', message, :yellow) if self.levels.include?(:warn)
47
+ end
48
+
49
+ def warn(message)
50
+ self.log('WARN', message, :yellow) if self.levels.include?(:warn)
51
+ end
52
+
53
+ def debug(message)
54
+ self.log('DEBUG', message, :purple) if self.levels.include?(:debug)
55
+ end
56
+
57
+ def info(message)
58
+ self.log('INFO', message, :green) if self.levels.include?(:info)
59
+ end
60
+
61
+ end
62
+
63
+ module_function
64
+
65
+ def logger
66
+ @logger ||= Logger.new
67
+ end
68
+
69
+ def logger=(log)
70
+ @logger = log
71
+ end
72
+
73
+ end
@@ -1,26 +1,57 @@
1
1
  module ProMotion
2
2
  module ViewHelper
3
3
  def set_attributes(element, args = {})
4
- args.each do |k, v|
5
- if v.is_a? Hash
6
- # TODO: Do this recursively
7
- v.each do |k2, v2|
8
- sub_element = element.send("#{k}")
9
- sub_element.send("#{k2}=", v2) if sub_element.respond_to?("#{k2}=")
10
- end
11
- elsif v.is_a? Array
12
- element.send("#{k}", *v) if element.respond_to?("#{k}")
13
- else
14
- element.send("#{k}=", v) if element.respond_to?("#{k}=")
4
+ args.each { |k, v| set_attribute(element, k, v) }
5
+ element
6
+ end
7
+
8
+ def set_attribute(element, k, v)
9
+ if v.is_a?(Hash) && element.respond_to?(k)
10
+ sub_element = element.send(k)
11
+ set_attributes sub_element, v
12
+ elsif v.is_a?(Array) && element.respond_to?("#{k}")
13
+ element.send("#{k}", *v)
14
+ elsif element.respond_to?("#{k}=")
15
+ element.send("#{k}=", v)
16
+ else
17
+ # Doesn't respond. Check if snake case.
18
+ if k.to_s.include?("_")
19
+ set_attribute(element, objective_c_method_name(k), v)
15
20
  end
16
21
  end
17
22
  element
18
23
  end
19
24
 
25
+ def objective_c_method_name(meth)
26
+ meth.split('_').inject([]){ |buffer,e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
27
+ end
28
+
29
+ def set_easy_attributes(parent, element, args={})
30
+ attributes = {}
31
+
32
+ if args[:resize]
33
+ attributes[:autoresizingMask] = UIViewAutoresizingNone
34
+ attributes[:autoresizingMask] |= UIViewAutoresizingFlexibleLeftMargin if args[:resize].include?(:left)
35
+ attributes[:autoresizingMask] |= UIViewAutoresizingFlexibleRightMargin if args[:resize].include?(:right)
36
+ attributes[:autoresizingMask] |= UIViewAutoresizingFlexibleTopMargin if args[:resize].include?(:top)
37
+ attributes[:autoresizingMask] |= UIViewAutoresizingFlexibleBottomMargin if args[:resize].include?(:bottom)
38
+ attributes[:autoresizingMask] |= UIViewAutoresizingFlexibleWidth if args[:resize].include?(:width)
39
+ attributes[:autoresizingMask] |= UIViewAutoresizingFlexibleHeight if args[:resize].include?(:height)
40
+ end
41
+
42
+ if [:left, :top, :width, :height].select{ |a| args[a] && args[a] != :auto }.length == 4
43
+ attributes[:frame] = CGRectMake(args[:left], args[:top], args[:width], args[:height])
44
+ end
45
+
46
+ set_attributes element, attributes
47
+ element
48
+ end
49
+
20
50
  def frame_from_array(array)
51
+ PM.logger.deprecated "`frame_from_array` is deprecated and will be removed. Use RubyMotion's built-in [[x, y], [width, height]]."
21
52
  return CGRectMake(array[0], array[1], array[2], array[3]) if array.length == 4
22
- Console.log(" - frame_from_array expects an array with four elements: [x, y, width, height]", with_color: Console::RED_COLOR)
23
- CGRectZero
53
+ PM.logger.error "frame_from_array expects an array with four elements: [x, y, width, height]"
54
+ CGRectZero.dup
24
55
  end
25
56
 
26
57
  def content_height(view)
@@ -35,5 +66,6 @@ module ProMotion
35
66
  end
36
67
  height
37
68
  end
69
+
38
70
  end
39
71
  end
@@ -0,0 +1,42 @@
1
+ module ProMotion::MotionTable
2
+ module RefreshableTable
3
+ def make_refreshable(params={})
4
+ pull_message = params[:pull_message] || "Pull to refresh"
5
+ @refreshing = params[:refreshing] || "Refreshing data..."
6
+ @updated_format = params[:updated_format] || "Last updated at %s"
7
+ @updated_time_format = params[:updated_time_format] || "%l:%M %p"
8
+ @refreshable_callback = params[:callback]|| :on_refresh
9
+
10
+ @refresh_control = UIRefreshControl.alloc.init
11
+ @refresh_control.attributedTitle = NSAttributedString.alloc.initWithString(pull_message)
12
+ @refresh_control.addTarget(self, action:'refreshView:', forControlEvents:UIControlEventValueChanged)
13
+ self.refreshControl = @refresh_control
14
+ end
15
+ alias :makeRefreshable :make_refreshable
16
+
17
+ ######### iOS methods, headless camel case #######
18
+
19
+ # UIRefreshControl Delegates
20
+ def refreshView(refresh)
21
+ refresh.attributedTitle = NSAttributedString.alloc.initWithString(@refreshing)
22
+ if @refreshable_callback && self.respond_to?(@refreshable_callback)
23
+ self.send(@refreshable_callback)
24
+ else
25
+ PM.logger.warn "You must implement the '#{@refreshable_callback}' method in your TableScreen."
26
+ end
27
+ end
28
+
29
+ def start_refreshing
30
+ return unless @refresh_control
31
+
32
+ @refresh_control.beginRefreshing
33
+ end
34
+
35
+ def end_refreshing
36
+ return unless @refresh_control
37
+
38
+ @refresh_control.attributedTitle = NSAttributedString.alloc.initWithString(sprintf(@updated_format, Time.now.strftime(@updated_time_format)))
39
+ @refresh_control.endRefreshing
40
+ end
41
+ end
42
+ end