motion-prime 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +83 -0
- data/README.md +67 -0
- data/Rakefile +23 -0
- data/app/app_delegate.rb +5 -0
- data/doc/SECTION.md +7 -0
- data/doc/STYLE.md +39 -0
- data/files/Gemfile +3 -0
- data/files/Rakefile +16 -0
- data/files/app/app_delegate.rb +4 -0
- data/files/app/screens/application_screen.rb +3 -0
- data/lib/motion-prime.rb +14 -0
- data/lib/view_styler.rb +141 -0
- data/motion-prime.gemspec +27 -0
- data/motion-prime/app_delegate.rb +56 -0
- data/motion-prime/elements/base.rb +94 -0
- data/motion-prime/elements/button.rb +7 -0
- data/motion-prime/elements/draw.rb +56 -0
- data/motion-prime/elements/draw/image.rb +43 -0
- data/motion-prime/elements/draw/label.rb +13 -0
- data/motion-prime/elements/image.rb +14 -0
- data/motion-prime/elements/label.rb +20 -0
- data/motion-prime/elements/text_field.rb +7 -0
- data/motion-prime/elements/text_view.rb +7 -0
- data/motion-prime/helpers/has_authorization.rb +10 -0
- data/motion-prime/helpers/has_search_bar.rb +25 -0
- data/motion-prime/models/base.rb +220 -0
- data/motion-prime/screens/_aliases_mixin.rb +32 -0
- data/motion-prime/screens/_base_mixin.rb +119 -0
- data/motion-prime/screens/_navigation_bar_mixin.rb +57 -0
- data/motion-prime/screens/_navigation_mixin.rb +118 -0
- data/motion-prime/screens/_orientations_mixin.rb +39 -0
- data/motion-prime/screens/base_screen.rb +22 -0
- data/motion-prime/screens/sidebar_container_screen.rb +58 -0
- data/motion-prime/sections/base.rb +101 -0
- data/motion-prime/sections/draw.rb +62 -0
- data/motion-prime/sections/form.rb +103 -0
- data/motion-prime/sections/form/base_field_section.rb +26 -0
- data/motion-prime/sections/form/password_field_section.rb +33 -0
- data/motion-prime/sections/form/select_field_section.rb +40 -0
- data/motion-prime/sections/form/string_field_section.rb +32 -0
- data/motion-prime/sections/form/submit_field_section.rb +20 -0
- data/motion-prime/sections/form/text_field_section.rb +33 -0
- data/motion-prime/sections/table.rb +97 -0
- data/motion-prime/sections/table/refresh_mixin.rb +13 -0
- data/motion-prime/styles/forms.rb +93 -0
- data/motion-prime/support/_key_value_store.rb +10 -0
- data/motion-prime/support/dm_button.rb +22 -0
- data/motion-prime/support/dm_cell_with_section.rb +12 -0
- data/motion-prime/support/dm_text_field.rb +30 -0
- data/motion-prime/support/dm_text_view.rb +93 -0
- data/motion-prime/support/dm_view_controller.rb +50 -0
- data/motion-prime/support/dm_view_with_section.rb +11 -0
- data/motion-prime/support/navigation_controller.rb +4 -0
- data/motion-prime/support/ui_search_bar_custom.rb +10 -0
- data/motion-prime/support/ui_view.rb +59 -0
- data/motion-prime/version.rb +3 -0
- data/motion-prime/views/layout.rb +45 -0
- data/motion-prime/views/styles.rb +44 -0
- data/motion-prime/views/view_builder.rb +80 -0
- data/motion-prime/views/view_styler.rb +141 -0
- data/resources/Default-568h@2x.png +0 -0
- data/spec/main_spec.rb +9 -0
- metadata +245 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
gem 'cocoapods', '0.19.1'
|
4
|
+
gem 'motion-cocoapods', '1.3.2'
|
5
|
+
|
6
|
+
# ruby sugar
|
7
|
+
gem 'sugarcube', '0.20.23'
|
8
|
+
gem 'motion-support', '0.2.4'
|
9
|
+
gem 'bubble-wrap', '1.3.0'
|
10
|
+
|
11
|
+
# modelds
|
12
|
+
gem 'nano-store', '0.6.3'
|
13
|
+
gemspec
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
motion-prime (0.1.0)
|
5
|
+
bubble-wrap
|
6
|
+
cocoapods
|
7
|
+
motion-cocoapods
|
8
|
+
motion-require
|
9
|
+
motion-support
|
10
|
+
nano-store
|
11
|
+
sugarcube
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: http://rubygems.org/
|
15
|
+
specs:
|
16
|
+
activesupport (3.2.13)
|
17
|
+
i18n (= 0.6.1)
|
18
|
+
multi_json (~> 1.0)
|
19
|
+
addressable (2.3.5)
|
20
|
+
bubble-wrap (1.3.0)
|
21
|
+
claide (0.2.0)
|
22
|
+
cocoapods (0.19.1)
|
23
|
+
activesupport (~> 3.2.13)
|
24
|
+
claide (~> 0.2.0)
|
25
|
+
cocoapods-core (= 0.19.1)
|
26
|
+
cocoapods-downloader (~> 0.1.0)
|
27
|
+
colored (~> 1.2)
|
28
|
+
escape (~> 0.0.4)
|
29
|
+
faraday (~> 0.8.1)
|
30
|
+
json (~> 1.7.3)
|
31
|
+
octokit (~> 1.7)
|
32
|
+
open4 (~> 1.3.0)
|
33
|
+
rake (~> 10.0.0)
|
34
|
+
xcodeproj (~> 0.5.5)
|
35
|
+
cocoapods-core (0.19.1)
|
36
|
+
activesupport (~> 3.2.13)
|
37
|
+
rake (~> 10.0.0)
|
38
|
+
cocoapods-downloader (0.1.1)
|
39
|
+
colored (1.2)
|
40
|
+
escape (0.0.4)
|
41
|
+
faraday (0.8.7)
|
42
|
+
multipart-post (~> 1.1)
|
43
|
+
faraday_middleware (0.9.0)
|
44
|
+
faraday (>= 0.7.4, < 0.9)
|
45
|
+
hashie (2.0.5)
|
46
|
+
i18n (0.6.1)
|
47
|
+
json (1.7.7)
|
48
|
+
motion-cocoapods (1.3.2)
|
49
|
+
cocoapods (>= 0.17.0)
|
50
|
+
motion-require (0.0.7)
|
51
|
+
motion-support (0.2.4)
|
52
|
+
motion-require (>= 0.0.6)
|
53
|
+
multi_json (1.7.7)
|
54
|
+
multipart-post (1.2.0)
|
55
|
+
nano-store (0.6.3)
|
56
|
+
motion-cocoapods (>= 1.2.1)
|
57
|
+
netrc (0.7.7)
|
58
|
+
octokit (1.25.0)
|
59
|
+
addressable (~> 2.2)
|
60
|
+
faraday (~> 0.8)
|
61
|
+
faraday_middleware (~> 0.9)
|
62
|
+
hashie (~> 2.0)
|
63
|
+
multi_json (~> 1.3)
|
64
|
+
netrc (~> 0.7.7)
|
65
|
+
open4 (1.3.0)
|
66
|
+
rake (10.0.4)
|
67
|
+
sugarcube (0.20.23)
|
68
|
+
xcodeproj (0.5.5)
|
69
|
+
activesupport (~> 3.2.13)
|
70
|
+
colored (~> 1.2)
|
71
|
+
|
72
|
+
PLATFORMS
|
73
|
+
ruby
|
74
|
+
|
75
|
+
DEPENDENCIES
|
76
|
+
bubble-wrap (= 1.3.0)
|
77
|
+
cocoapods (= 0.19.1)
|
78
|
+
motion-cocoapods (= 1.3.2)
|
79
|
+
motion-prime!
|
80
|
+
motion-support (= 0.2.4)
|
81
|
+
nano-store (= 0.6.3)
|
82
|
+
rake
|
83
|
+
sugarcube (= 0.20.23)
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# MotionPrime
|
2
|
+
|
3
|
+
![Prime](https://s3-us-west-2.amazonaws.com/webmate/assets/prime.jpg)
|
4
|
+
|
5
|
+
MotionPrime is yet another framework written on RubyMotion.
|
6
|
+
|
7
|
+
The main feature of MotionPrime is one more level on UI elements: Section.
|
8
|
+
"Section" is something like "Partial" in Ruby On Rails, but it's smarter and will help you build application UI.
|
9
|
+
|
10
|
+
## Getting Started
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'motion-prime'
|
15
|
+
|
16
|
+
Or create MotionPrime project:
|
17
|
+
|
18
|
+
$ motion create --template=git@github.com:droidlabs/motion-prime.git myapp
|
19
|
+
|
20
|
+
## Hello World (Sample)
|
21
|
+
|
22
|
+
# app/app_delegate.rb
|
23
|
+
class AppDelegate < MotionPrime::BaseAppDelegate
|
24
|
+
def on_load(app, options)
|
25
|
+
open_root_screen MainScreen.new
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# app/screens/main_screen.rb
|
30
|
+
class MainScreen < ApplicationScreen
|
31
|
+
title 'Main screen'
|
32
|
+
|
33
|
+
def render
|
34
|
+
@main_section = MyProfileSection.new(model: User.first)
|
35
|
+
@main_section.render(to: self)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# app/sections/my_profile.rb
|
40
|
+
class MyProfileSection < MotionPrime::BaseSection
|
41
|
+
element :title, text: proc { Hello World }
|
42
|
+
element :avatar, image: "images/avatar.png", type: :image
|
43
|
+
end
|
44
|
+
|
45
|
+
# app/styles/my_profile.rb
|
46
|
+
MotionPrime::Styles.define :my_profile do
|
47
|
+
style :title,
|
48
|
+
width: 300, height: 20, color: :black,
|
49
|
+
top: 10, left: 5, background_color: :white
|
50
|
+
|
51
|
+
style :avatar,
|
52
|
+
width: 90, height: 90, top: 40, left: 5
|
53
|
+
end
|
54
|
+
|
55
|
+
## Contributing
|
56
|
+
|
57
|
+
1. Fork it
|
58
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
59
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
60
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
61
|
+
5. Create new Pull Request
|
62
|
+
|
63
|
+
## Thanks for using MotionPrime!
|
64
|
+
|
65
|
+
Hope, you'll enjoy MotionPrime!
|
66
|
+
|
67
|
+
Cheers, [Droid Labs](http://droidlabs.pro).
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
$:.unshift("/Library/RubyMotion/lib")
|
3
|
+
require 'motion/project/template/ios'
|
4
|
+
require "rubygems"
|
5
|
+
require "bundler"
|
6
|
+
require "bundler/gem_tasks"
|
7
|
+
require 'motion-cocoapods'
|
8
|
+
Bundler.setup
|
9
|
+
Bundler.require
|
10
|
+
require 'motion-support'
|
11
|
+
require 'nano-store'
|
12
|
+
require 'motion-prime'
|
13
|
+
|
14
|
+
Motion::Project::App.setup do |app|
|
15
|
+
app.name = 'MotionPrime'
|
16
|
+
app.pods do
|
17
|
+
pod 'PKRevealController'
|
18
|
+
pod 'NanoStore', '~> 2.6.0'
|
19
|
+
pod 'SDWebImage'
|
20
|
+
pod 'SVPullToRefresh'
|
21
|
+
pod 'MBAlertView'
|
22
|
+
end
|
23
|
+
end
|
data/app/app_delegate.rb
ADDED
data/doc/SECTION.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
== GENERAL
|
2
|
+
|
3
|
+
Section is the most important part of MotionPrime. Section is container for elements (views), grouped for some aim.
|
4
|
+
|
5
|
+
== Section attributes
|
6
|
+
|
7
|
+
* @name - this option is used by part of default styles of any contained element. Check out STYLES doc for more information. default value: section class name without "section word". E.g. LoginFormSection have default name: "login_form"
|
data/doc/STYLE.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
== Default styling names for elements.
|
2
|
+
|
3
|
+
=== Screen
|
4
|
+
* "base_screen"
|
5
|
+
* "%screen class name%", e.g. "login_screen"
|
6
|
+
|
7
|
+
=== Table
|
8
|
+
* "base_table"
|
9
|
+
* "%section_name%", e.g. "tasks_table"
|
10
|
+
|
11
|
+
=== Table cell
|
12
|
+
* "base_table_cell"
|
13
|
+
* "%section_name%_cell", e.g. "tasks_table_cell"
|
14
|
+
* "%cell_section_name%", e.g. "tasks_item"
|
15
|
+
|
16
|
+
=== Form (basically it's table)
|
17
|
+
* "base_form"
|
18
|
+
* "%section_name%", e.g. "login_form"
|
19
|
+
|
20
|
+
=== Form field (basically it's table cell)
|
21
|
+
* "base_form_field"
|
22
|
+
* "%section_name%_field", e.g. "login_form_field"
|
23
|
+
|
24
|
+
=== Form field: label
|
25
|
+
* "base_field_label"
|
26
|
+
* "base_%field_type%_field_label", e.g. "base_string_field_label"
|
27
|
+
* "%section_name%_field_label", e.g. "login_form_field_label"
|
28
|
+
* "%section_name%_%field_name%_field_label", e.g. "login_form_email_field_label"
|
29
|
+
|
30
|
+
=== Form field: input
|
31
|
+
* "base_field_input"
|
32
|
+
* "base_%field_name%_field_label", e.g. "base_string_field_input"
|
33
|
+
* "%section_name%_field_input", e.g. "login_form_field_input"
|
34
|
+
* "%section_name%_%field_name%_field_input", e.g. "login_form_email_field_input"
|
35
|
+
|
36
|
+
=== Form field: button
|
37
|
+
* "base_submit_button"
|
38
|
+
* "%section_name%_submit_button", e.g. "login_form_submit_button"
|
39
|
+
* "%section_name%_%field_name%_button", e.g. "login_form_signup_button"
|
data/files/Gemfile
ADDED
data/files/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
$:.unshift("/Library/RubyMotion/lib")
|
3
|
+
require 'motion/project/template/ios'
|
4
|
+
require "rubygems"
|
5
|
+
require 'bundler'
|
6
|
+
Bundler.require
|
7
|
+
require 'motion-prime'
|
8
|
+
|
9
|
+
Motion::Project::App.setup do |app|
|
10
|
+
# Use `rake config' to see complete project settings.
|
11
|
+
app.name = 'MotionPrimeProject'
|
12
|
+
|
13
|
+
app.pods do
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
data/lib/motion-prime.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'motion-require'
|
2
|
+
|
3
|
+
Motion::Require.all(Dir.glob(File.expand_path('../../motion-prime/**/*.rb', __FILE__)))
|
4
|
+
|
5
|
+
Motion::Project::App.setup do |app|
|
6
|
+
app.pods do
|
7
|
+
pod 'PKRevealController'
|
8
|
+
pod 'NanoStore', '~> 2.6.0'
|
9
|
+
pod 'SDWebImage'
|
10
|
+
pod 'SVPullToRefresh'
|
11
|
+
pod 'MBAlertView'
|
12
|
+
end
|
13
|
+
app.detect_dependencies = false
|
14
|
+
end
|
data/lib/view_styler.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
module MotionPrime
|
2
|
+
class ViewStyler
|
3
|
+
attr_reader :view, :options
|
4
|
+
|
5
|
+
def initialize(view, bounds = CGRectZero, options = {})
|
6
|
+
@options = Styles.extend_and_normalize_options options
|
7
|
+
@view = view
|
8
|
+
calculate_frame_for(bounds) if @options.delete(:calculate_frame)
|
9
|
+
end
|
10
|
+
|
11
|
+
def apply
|
12
|
+
convert_primitives_to_objects(options)
|
13
|
+
setValuesForKeysWithDictionary(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def convert_primitives_to_objects(options)
|
17
|
+
options.each do |k,v|
|
18
|
+
options[k] = STRUCTS_MAP[v.class].call(v) if STRUCTS_MAP.has_key?(v.class)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def calculate_frame_for(bounds)
|
23
|
+
width = options.delete(:width)
|
24
|
+
height = options.delete(:height)
|
25
|
+
top = options.delete(:top)
|
26
|
+
right = options.delete(:right)
|
27
|
+
bottom = options.delete(:bottom)
|
28
|
+
left = options.delete(:left)
|
29
|
+
|
30
|
+
if width.nil? && height.nil? && right.nil? && bottom.nil?
|
31
|
+
options[:frame] = CGRectZero
|
32
|
+
else
|
33
|
+
frame = CGRectZero
|
34
|
+
max_width = bounds.size.width
|
35
|
+
max_height = bounds.size.height
|
36
|
+
width = 0.0 if width.nil?
|
37
|
+
height = 0.0 if height.nil?
|
38
|
+
|
39
|
+
# calculate left and right if width is relative, e.g 0.7
|
40
|
+
if width > 0 && width <= 1
|
41
|
+
if right.nil?
|
42
|
+
left ||= 0
|
43
|
+
right = max_width - max_width * width
|
44
|
+
else
|
45
|
+
left = max_width - max_width * width
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# calculate top and bottom if height is relative, e.g 0.7
|
50
|
+
if height > 0 && height <= 1
|
51
|
+
if bottom.nil?
|
52
|
+
top ||= 0
|
53
|
+
bottom = max_height - max_height * height
|
54
|
+
else
|
55
|
+
top = max_height - max_height * height
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
mask = UIViewAutoresizingNone
|
60
|
+
mask |= UIViewAutoresizingFlexibleTopMargin if top.nil?
|
61
|
+
mask |= UIViewAutoresizingFlexibleLeftMargin if left.nil?
|
62
|
+
mask |= UIViewAutoresizingFlexibleBottomMargin if bottom.nil?
|
63
|
+
mask |= UIViewAutoresizingFlexibleRightMargin if right.nil?
|
64
|
+
mask |= UIViewAutoresizingFlexibleWidth if !left.nil? && !right.nil?
|
65
|
+
mask |= UIViewAutoresizingFlexibleHeight if !top.nil? && !bottom.nil?
|
66
|
+
|
67
|
+
if !left.nil? && !right.nil?
|
68
|
+
frame.origin.x = left
|
69
|
+
frame.size.width = max_width - left - right
|
70
|
+
elsif !right.nil?
|
71
|
+
frame.origin.x = max_width - width - right
|
72
|
+
frame.size.width = width
|
73
|
+
elsif !left.nil?
|
74
|
+
frame.origin.x = left
|
75
|
+
frame.size.width = width
|
76
|
+
else
|
77
|
+
frame.origin.x = max_width / 2 - width / 2
|
78
|
+
frame.size.width = width
|
79
|
+
end
|
80
|
+
|
81
|
+
if !top.nil? && !bottom.nil?
|
82
|
+
frame.origin.y = top
|
83
|
+
frame.size.height = max_height - top - bottom
|
84
|
+
elsif !bottom.nil?
|
85
|
+
frame.origin.y = max_height - height - bottom
|
86
|
+
frame.size.height = height
|
87
|
+
elsif !top.nil?
|
88
|
+
frame.origin.y = top
|
89
|
+
frame.size.height = height
|
90
|
+
else
|
91
|
+
frame.origin.y = max_height / 2 - height / 2
|
92
|
+
frame.size.height = height
|
93
|
+
end
|
94
|
+
|
95
|
+
options[:frame] = frame
|
96
|
+
options[:autoresizingMask] = mask
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def setValue(value, forUndefinedKey: key)
|
101
|
+
return if value.nil?
|
102
|
+
# ignore options
|
103
|
+
return if key == 'size_to_fit' && view.is_a?(UILabel)
|
104
|
+
return if (key == 'url' || key == 'default') && view.is_a?(UIImageView)
|
105
|
+
|
106
|
+
# apply options
|
107
|
+
if key.end_with?('title_color')
|
108
|
+
view.setTitleColor value.uicolor, forState: UIControlStateNormal
|
109
|
+
elsif key.end_with?('title_shadow_color')
|
110
|
+
view.setTitleShadowColor value.uicolor, forState: UIControlStateNormal
|
111
|
+
elsif key.end_with?('color')
|
112
|
+
color = value.uicolor
|
113
|
+
color = color.cgcolor if view.is_a?(CALayer)
|
114
|
+
view.send :"#{key.camelize(:lower)}=", color
|
115
|
+
elsif key.end_with?('background_image')
|
116
|
+
if view.is_a?(UIButton)
|
117
|
+
view.setBackgroundImage value.uiimage, forState: UIControlStateNormal
|
118
|
+
elsif view.is_a?(UISearchBar) && key == 'search_field_background_image'
|
119
|
+
view.setSearchFieldBackgroundImage value.uiimage, forState: UIControlStateNormal
|
120
|
+
else
|
121
|
+
view.setBackgroundColor value.uiimage.uicolor
|
122
|
+
end
|
123
|
+
elsif key.end_with?('image')
|
124
|
+
view.setValue value.uiimage, forKey: key.camelize
|
125
|
+
elsif value.is_a?(Hash)
|
126
|
+
self.class.new(view.send(key.camelize(:lower).to_sym), nil, value).apply
|
127
|
+
else
|
128
|
+
view.setValue value, forKey: key.camelize(:lower)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
STRUCTS_MAP = {
|
133
|
+
CGAffineTransform => Proc.new {|v| NSValue.valueWithCGAffineTransform(v) },
|
134
|
+
CGPoint => Proc.new {|v| NSValue.valueWithCGPoint(v) },
|
135
|
+
CGRect => Proc.new {|v| NSValue.valueWithCGRect(v) },
|
136
|
+
CGSize => Proc.new {|v| NSValue.valueWithCGSize(v) },
|
137
|
+
UIEdgeInsets => Proc.new {|v| NSValue.valueWithUIEdgeInsets(v) },
|
138
|
+
UIOffset => Proc.new {|v| NSValue.valueWithUIOffset(v) }
|
139
|
+
}
|
140
|
+
end
|
141
|
+
end
|