ProMotion 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +5 -1
- data/README.md +237 -138
- data/Rakefile +4 -9
- data/app/app_delegate.rb +3 -0
- data/app/screens/basic_screen.rb +27 -0
- data/lib/ProMotion.rb +3 -5
- data/lib/ProMotion/cocoatouch/SplitViewController.rb +25 -0
- data/lib/ProMotion/cocoatouch/TableViewController.rb +5 -5
- data/lib/ProMotion/cocoatouch/ViewController.rb +5 -5
- data/lib/ProMotion/{app_delegate.rb → delegate.rb} +23 -23
- data/lib/ProMotion/helpers/console.rb +6 -4
- data/lib/ProMotion/helpers/logger.rb +73 -0
- data/lib/ProMotion/helpers/view_helper.rb +45 -13
- data/lib/ProMotion/screen_helpers/_tables/_refreshable_table.rb +42 -0
- data/lib/ProMotion/screen_helpers/_tables/_searchable_table.rb +2 -2
- data/lib/ProMotion/screen_helpers/_tables/_sectioned_table.rb +46 -41
- data/lib/ProMotion/screen_helpers/_tables/grouped_table.rb +2 -1
- data/lib/ProMotion/screen_helpers/_tables/plain_table.rb +1 -0
- data/lib/ProMotion/screen_helpers/screen_elements.rb +16 -11
- data/lib/ProMotion/screen_helpers/screen_navigation.rb +15 -16
- data/lib/ProMotion/screen_helpers/screen_tabs.rb +20 -16
- data/lib/ProMotion/screen_helpers/split_screen.rb +42 -0
- data/lib/ProMotion/screens/_screen_module.rb +44 -35
- data/lib/ProMotion/screens/_table_screen_module.rb +18 -1
- data/lib/ProMotion/screens/screen.rb +1 -1
- data/lib/ProMotion/version.rb +1 -1
- data/spec/helpers/table_screen.rb +48 -0
- data/spec/helpers/table_screen_refreshable.rb +11 -0
- data/spec/helpers/table_screen_searchable.rb +5 -0
- data/spec/helpers/test_delegate.rb +9 -0
- data/spec/ios_version_spec.rb +6 -6
- data/spec/logger_spec.rb +68 -0
- data/spec/main_spec.rb +1 -1
- data/spec/screen_helpers_spec.rb +35 -6
- data/spec/{view_controller_spec.rb → screen_spec.rb} +1 -1
- data/spec/split_screen_in_tab_bar_spec.rb +49 -0
- data/spec/split_screen_open_screen_spec.rb +46 -0
- data/spec/split_screen_spec.rb +35 -0
- data/spec/table_screen_spec.rb +72 -0
- data/spec/view_helper_spec.rb +112 -8
- metadata +29 -8
- data/lib/ProMotion/helpers/tab_bar.rb +0 -115
- 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.
|
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 = "
|
16
|
-
app.device_family = [:
|
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
@@ -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
|
-
|
13
|
-
|
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
|
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
|
-
|
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 ||=
|
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
|
35
|
-
|
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
|
45
|
-
home(
|
42
|
+
def open_screen(screen, args={})
|
43
|
+
home(screen)
|
46
44
|
open_home_screen
|
47
45
|
end
|
48
|
-
alias :
|
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
|
-
|
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
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
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
|