ProMotion 1.0.4 → 1.1.0.rc1

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/ProMotion.gemspec +3 -2
  3. data/README.md +10 -7
  4. data/app/screens/basic_screen.rb +12 -0
  5. data/bin/promotion +45 -0
  6. data/lib/ProMotion/cocoatouch/table_view_controller.rb +2 -2
  7. data/lib/ProMotion/cocoatouch/view_controller.rb +2 -2
  8. data/lib/ProMotion/containers/split_screen.rb +6 -1
  9. data/lib/ProMotion/containers/tabs.rb +12 -1
  10. data/lib/ProMotion/delegate/delegate_module.rb +16 -2
  11. data/lib/ProMotion/delegate/delegate_notifications.rb +7 -7
  12. data/lib/ProMotion/map/map_screen_annotation.rb +10 -1
  13. data/lib/ProMotion/map/map_screen_module.rb +8 -3
  14. data/lib/ProMotion/pro_motion.rb +1 -1
  15. data/lib/ProMotion/screen/screen_module.rb +35 -16
  16. data/lib/ProMotion/screen/screen_navigation.rb +11 -13
  17. data/lib/ProMotion/table/extensions/indexable.rb +2 -2
  18. data/lib/ProMotion/table/extensions/searchable.rb +4 -3
  19. data/lib/ProMotion/table/grouped_table.rb +7 -2
  20. data/lib/ProMotion/table/table.rb +54 -47
  21. data/lib/ProMotion/thirdparty/formotion_screen.rb +5 -3
  22. data/lib/ProMotion/version.rb +1 -1
  23. data/lib/ProMotion/view/styling.rb +33 -20
  24. data/resources/test.png +0 -0
  25. data/spec/functional/func_map_screen_spec.rb +60 -3
  26. data/spec/functional/func_screen_spec.rb +29 -6
  27. data/spec/functional/func_searchable_table_spec.rb +13 -1
  28. data/spec/functional/func_split_screen_spec.rb +8 -0
  29. data/spec/functional/func_table_screen_spec.rb +28 -23
  30. data/spec/functional/func_web_screen_spec.rb +1 -1
  31. data/spec/helpers/custom_title_view.rb +4 -0
  32. data/spec/helpers/home_screen.rb +6 -0
  33. data/spec/helpers/table_screen.rb +23 -27
  34. data/spec/helpers/table_screen_formotion.rb +5 -0
  35. data/spec/helpers/table_screen_searchable.rb +10 -0
  36. data/spec/helpers/test_delegate_colors.rb +17 -0
  37. data/spec/helpers/test_helper.rb +5 -0
  38. data/spec/unit/delegate_spec.rb +79 -1
  39. data/spec/unit/map_spec.rb +22 -0
  40. data/spec/unit/screen_helpers_spec.rb +44 -35
  41. data/spec/unit/screen_spec.rb +45 -6
  42. data/spec/unit/split_screen_open_screen_spec.rb +1 -1
  43. data/spec/unit/tables/formotion_screen_spec.rb +6 -0
  44. data/spec/unit/tables/table_module_spec.rb +28 -2
  45. data/spec/unit/tables/table_view_cell_spec.rb +5 -4
  46. data/spec/unit/view_helper_spec.rb +21 -10
  47. metadata +28 -6
  48. data/resources/test.jpeg +0 -0
@@ -23,8 +23,8 @@ module ProMotion
23
23
  elsif args[:in_tab] && self.tab_bar
24
24
  present_view_controller_in_tab_bar_controller screen, args[:in_tab]
25
25
 
26
- elsif self.navigation_controller
27
- push_view_controller screen
26
+ elsif self.navigationController
27
+ push_view_controller screen, self.navigationController, args[:animated].nil? ? true : args[:animated]
28
28
 
29
29
  else
30
30
  open_root_screen (screen.navigationController || screen)
@@ -55,7 +55,7 @@ module ProMotion
55
55
  if self.modal?
56
56
  close_modal_screen args
57
57
 
58
- elsif self.navigation_controller
58
+ elsif self.navigationController
59
59
  close_nav_screen args
60
60
  send_on_return(args) # TODO: this would be better implemented in a callback or view_did_disappear.
61
61
 
@@ -76,14 +76,13 @@ module ProMotion
76
76
  end
77
77
  end
78
78
 
79
- def push_view_controller(vc, nav_controller=nil)
80
- unless self.navigation_controller
79
+ def push_view_controller(vc, nav_controller=nil, animated=true)
80
+ unless self.navigationController
81
81
  PM.logger.error "You need a nav_bar if you are going to push #{vc.to_s} onto it."
82
82
  end
83
- nav_controller ||= self.navigation_controller
83
+ nav_controller ||= self.navigationController
84
84
  vc.first_screen = false if vc.respond_to?(:first_screen=)
85
- vc.navigation_controller = nav_controller if vc.respond_to?(:navigation_controller=)
86
- nav_controller.pushViewController(vc, animated: true)
85
+ nav_controller.pushViewController(vc, animated: animated)
87
86
  end
88
87
 
89
88
  protected
@@ -113,7 +112,7 @@ module ProMotion
113
112
 
114
113
  def ensure_wrapper_controller_in_place(screen, args={})
115
114
  unless args[:close_all] || args[:modal] || args[:in_detail] || args[:in_master]
116
- screen.navigation_controller ||= self.navigation_controller if screen.respond_to?("navigation_controller=")
115
+ screen.navigationController ||= self.navigationController
117
116
  screen.tab_bar ||= self.tab_bar if screen.respond_to?("tab_bar=")
118
117
  end
119
118
  end
@@ -127,7 +126,6 @@ module ProMotion
127
126
  if vc
128
127
 
129
128
  if vc.is_a?(UINavigationController)
130
- screen.navigation_controller = vc if screen.respond_to?("navigation_controller=")
131
129
  push_view_controller(screen, vc)
132
130
  else
133
131
  # TODO: This should probably open the vc, shouldn't it?
@@ -150,12 +148,12 @@ module ProMotion
150
148
  def close_nav_screen(args={})
151
149
  args[:animated] = true unless args.has_key?(:animated)
152
150
  if args[:to_screen] == :root
153
- self.navigation_controller.popToRootViewControllerAnimated args[:animated]
151
+ self.navigationController.popToRootViewControllerAnimated args[:animated]
154
152
  elsif args[:to_screen] && args[:to_screen].is_a?(UIViewController)
155
153
  self.parent_screen = args[:to_screen]
156
- self.navigation_controller.popToViewController(args[:to_screen], animated: args[:animated])
154
+ self.navigationController.popToViewController(args[:to_screen], animated: args[:animated])
157
155
  else
158
- self.navigation_controller.popViewControllerAnimated(args[:animated])
156
+ self.navigationController.popViewControllerAnimated(args[:animated])
159
157
  end
160
158
  end
161
159
 
@@ -2,9 +2,9 @@ module ProMotion
2
2
  module Table
3
3
  module Indexable
4
4
  def table_data_index
5
- return nil if @promotion_table_data.filtered || !self.class.get_indexable
5
+ return nil if self.promotion_table_data.filtered || !self.class.get_indexable
6
6
 
7
- index = @promotion_table_data.sections.collect{ |section| section[:title][0] }
7
+ index = self.promotion_table_data.sections.collect{ |section| section[:title][0] }
8
8
  index.unshift("{search}") if self.class.get_searchable
9
9
  index
10
10
  end
@@ -42,19 +42,20 @@ module ProMotion
42
42
  ######### iOS methods, headless camel case #######
43
43
 
44
44
  def searchDisplayController(controller, shouldReloadTableForSearchString:search_string)
45
- @promotion_table_data.search(search_string)
45
+ self.promotion_table_data.search(search_string)
46
46
  true
47
47
  end
48
48
 
49
49
  def searchDisplayControllerWillEndSearch(controller)
50
- @promotion_table_data.stop_searching
51
- @promotion_table_data_data = nil
50
+ self.promotion_table_data.stop_searching
52
51
  self.table_view.setScrollEnabled true
53
52
  self.table_view.reloadData
53
+ @table_search_display_controller.delegate.will_end_search if @table_search_display_controller.delegate.respond_to? "will_end_search"
54
54
  end
55
55
 
56
56
  def searchDisplayControllerWillBeginSearch(controller)
57
57
  self.table_view.setScrollEnabled false
58
+ @table_search_display_controller.delegate.will_begin_search if @table_search_display_controller.delegate.respond_to? "will_begin_search"
58
59
  end
59
60
  end
60
61
  end
@@ -1,7 +1,12 @@
1
1
  module ProMotion
2
2
  module GroupedTable
3
- def table_style
4
- UITableViewStyleGrouped
3
+ module GroupedTableClassMethods
4
+ def table_style
5
+ UITableViewStyleGrouped
6
+ end
7
+ end
8
+ def self.included(base)
9
+ base.extend(GroupedTableClassMethods)
5
10
  end
6
11
  end
7
12
  end
@@ -6,22 +6,14 @@ module ProMotion
6
6
  include ProMotion::Table::Refreshable
7
7
  include ProMotion::Table::Indexable
8
8
 
9
- def table_view
10
- @table_view ||= begin
11
- t = UITableView.alloc.initWithFrame(self.view.frame, style: table_style)
12
- t.dataSource = self
13
- t.delegate = self
14
- t
15
- end
16
- end
9
+ attr_reader :promotion_table_data
17
10
 
18
- def table_style
19
- UITableViewStylePlain
11
+ def table_view
12
+ self.view
20
13
  end
21
14
 
22
15
  def screen_setup
23
16
  check_table_data
24
- set_up_table_view
25
17
  set_up_searchable
26
18
  set_up_refreshable
27
19
  end
@@ -30,11 +22,8 @@ module ProMotion
30
22
  PM.logger.error "Missing #table_data method in TableScreen #{self.class.to_s}." unless self.respond_to?(:table_data)
31
23
  end
32
24
 
33
- def set_up_table_view
34
- # before access self.table_data, create UITableView and call on_load
35
- table_view
36
-
37
- self.view = self.create_table_view_from_data(self.table_data)
25
+ def promotion_table_data
26
+ @promotion_table_data ||= TableData.new(table_data, table_view)
38
27
  end
39
28
 
40
29
  def set_up_searchable
@@ -53,37 +42,31 @@ module ProMotion
53
42
  end
54
43
  end
55
44
 
56
- def create_table_view_from_data(data)
57
- @promotion_table_data = TableData.new(data, table_view)
58
- table_view
59
- end
60
-
61
45
  def searching?
62
- @promotion_table_data.filtered
46
+ self.promotion_table_data.filtered
63
47
  end
64
48
 
65
49
  def original_search_string
66
- @promotion_table_data.original_search_string
50
+ self.promotion_table_data.original_search_string
67
51
  end
68
52
 
69
53
  def search_string
70
- @promotion_table_data.search_string
54
+ self.promotion_table_data.search_string
71
55
  end
72
56
 
73
57
  def update_table_view_data(data)
74
- create_table_view_from_data(data) unless @promotion_table_data
75
- @promotion_table_data.data = data
58
+ self.promotion_table_data.data = data
76
59
  table_view.reloadData
77
60
  end
78
61
 
79
62
  # Methods to retrieve data
80
63
 
81
64
  def section_at_index(index)
82
- @promotion_table_data.section(index)
65
+ self.promotion_table_data.section(index)
83
66
  end
84
67
 
85
68
  def cell_at_section_and_index(section, index)
86
- @promotion_table_data.cell(section: section, index: index)
69
+ self.promotion_table_data.cell(section: section, index: index)
87
70
  end
88
71
 
89
72
  def trigger_action(action, arguments)
@@ -121,9 +104,9 @@ module ProMotion
121
104
 
122
105
  index_paths.each do |index_path|
123
106
  delete_cell = false
124
- delete_cell = send(:on_cell_deleted, @promotion_table_data.cell(index_path: index_path)) if self.respond_to?("on_cell_deleted:")
107
+ delete_cell = send(:on_cell_deleted, self.promotion_table_data.cell(index_path: index_path)) if self.respond_to?("on_cell_deleted:")
125
108
  unless delete_cell == false
126
- @promotion_table_data.delete_cell(index_path: index_path)
109
+ self.promotion_table_data.delete_cell(index_path: index_path)
127
110
  deletable_index_paths << index_path
128
111
  end
129
112
  end
@@ -136,7 +119,7 @@ module ProMotion
136
119
  params[:index] = params[:index_path].row
137
120
  end
138
121
 
139
- data_cell = @promotion_table_data.cell(section: params[:section], index: params[:index])
122
+ data_cell = self.promotion_table_data.cell(section: params[:section], index: params[:index])
140
123
  return UITableViewCell.alloc.init unless data_cell # No data?
141
124
 
142
125
  table_cell = create_table_cell(data_cell)
@@ -165,28 +148,24 @@ module ProMotion
165
148
 
166
149
  ########## Cocoa touch methods #################
167
150
  def numberOfSectionsInTableView(table_view)
168
- return @promotion_table_data.data.size
151
+ Array(self.promotion_table_data.data).length
169
152
  end
170
153
 
171
154
  # Number of cells
172
155
  def tableView(table_view, numberOfRowsInSection:section)
173
- return @promotion_table_data.section_length(section)
174
- 0
156
+ self.promotion_table_data.section_length(section)
175
157
  end
176
158
 
177
159
  def tableView(table_view, titleForHeaderInSection:section)
178
- return section_at_index(section)[:title] if section_at_index(section) && section_at_index(section)[:title]
160
+ section = section_at_index(section) || return
161
+ section[:title]
179
162
  end
180
163
 
181
164
  # Set table_data_index if you want the right hand index column (jumplist)
182
165
  def sectionIndexTitlesForTableView(table_view)
183
- return nil if @promotion_table_data.filtered
184
-
185
- if self.respond_to?(:table_data_index)
186
- self.table_data_index
187
- else
188
- nil
189
- end
166
+ return if self.promotion_table_data.filtered
167
+ return self.table_data_index if self.respond_to?(:table_data_index)
168
+ nil
190
169
  end
191
170
 
192
171
  def tableView(table_view, cellForRowAtIndexPath:index_path)
@@ -194,18 +173,18 @@ module ProMotion
194
173
  end
195
174
 
196
175
  def tableView(table_view, willDisplayCell: table_cell, forRowAtIndexPath: index_path)
197
- data_cell = @promotion_table_data.cell(index_path: index_path)
176
+ data_cell = self.promotion_table_data.cell(index_path: index_path)
198
177
  table_cell.backgroundColor = data_cell[:background_color] || UIColor.whiteColor
199
178
  table_cell.send(:restyle!) if table_cell.respond_to?(:restyle!)
200
179
  end
201
180
 
202
181
  def tableView(table_view, heightForRowAtIndexPath:index_path)
203
- (@promotion_table_data.cell(index_path: index_path)[:height] || table_view.rowHeight).to_f
182
+ (self.promotion_table_data.cell(index_path: index_path)[:height] || table_view.rowHeight).to_f
204
183
  end
205
184
 
206
185
  def tableView(table_view, didSelectRowAtIndexPath:index_path)
207
- data_cell = @promotion_table_data.cell(index_path: index_path)
208
- table_view.deselectRowAtIndexPath(index_path, animated: true)
186
+ data_cell = self.promotion_table_data.cell(index_path: index_path)
187
+ table_view.deselectRowAtIndexPath(index_path, animated: true) unless data_cell[:keep_selection] == true
209
188
 
210
189
  data_cell[:arguments] ||= {}
211
190
  data_cell[:arguments][:cell] = data_cell if data_cell[:arguments].is_a?(Hash) # TODO: Should we really do this?
@@ -214,7 +193,7 @@ module ProMotion
214
193
  end
215
194
 
216
195
  def tableView(table_view, editingStyleForRowAtIndexPath: index_path)
217
- data_cell = @promotion_table_data.cell(index_path: index_path)
196
+ data_cell = self.promotion_table_data.cell(index_path: index_path)
218
197
 
219
198
  case data_cell[:editing_style]
220
199
  when nil, :none
@@ -250,6 +229,30 @@ module ProMotion
250
229
  delete_row(index_paths, animation)
251
230
  end
252
231
 
232
+ # Section view methods
233
+ def tableView(table_view, viewForHeaderInSection: index)
234
+ section = section_at_index(index)
235
+
236
+ if section[:title_view]
237
+ klass = section[:title_view]
238
+ view = klass.new if klass.respond_to?(:new)
239
+ view.title = section[:title] if view.respond_to?(:title=)
240
+ view
241
+ else
242
+ nil
243
+ end
244
+ end
245
+
246
+ def tableView(table_view, heightForHeaderInSection: index)
247
+ section = section_at_index(index)
248
+
249
+ if section[:title_view] || (section[:title] && !section[:title].empty?)
250
+ section[:title_view_height] || tableView.sectionHeaderHeight
251
+ else
252
+ 0.0
253
+ end
254
+ end
255
+
253
256
  protected
254
257
 
255
258
  def map_row_animation_symbol(symbol)
@@ -268,6 +271,10 @@ module ProMotion
268
271
  end
269
272
 
270
273
  module TableClassMethods
274
+ def table_style
275
+ UITableViewStylePlain
276
+ end
277
+
271
278
  # Searchable
272
279
  def searchable(params={})
273
280
  @searchable_params = params
@@ -2,7 +2,7 @@ module ProMotion
2
2
  if defined?(Formotion) && defined?(Formotion::FormController)
3
3
  class FormotionScreen < Formotion::FormController
4
4
  include ProMotion::ScreenModule
5
-
5
+
6
6
  def self.new(args = {})
7
7
  s = self.alloc.initWithStyle(UITableViewStyleGrouped)
8
8
  s.on_create(args) if s.respond_to?(:on_create)
@@ -19,6 +19,8 @@ module ProMotion
19
19
  s.tableView.allowsSelectionDuringEditing = true
20
20
  s.title = t
21
21
 
22
+ s.form.on_submit { |form| s.on_submit(form) if s.respond_to?(:on_submit) }
23
+
22
24
  s
23
25
  end
24
26
 
@@ -28,11 +30,11 @@ module ProMotion
28
30
  self.form.controller = self
29
31
  self.tableView.reloadData
30
32
  end
31
-
33
+
32
34
  def screen_setup
33
35
  self.title = self.class.send(:get_title)
34
36
  end
35
-
37
+
36
38
  def loadView
37
39
  super
38
40
  self.send(:on_load) if self.respond_to?(:on_load)
@@ -1,3 +1,3 @@
1
1
  module ProMotion
2
- VERSION = "1.0.4" unless defined?(ProMotion::VERSION)
2
+ VERSION = "1.1.0.rc1" unless defined?(ProMotion::VERSION)
3
3
  end
@@ -1,8 +1,9 @@
1
1
  module ProMotion
2
2
  module Styling
3
3
  include Conversions
4
-
4
+
5
5
  def set_attributes(element, args = {})
6
+ args = get_attributes_from_symbol(args)
6
7
  args.each { |k, v| set_attribute(element, k, v) }
7
8
  element
8
9
  end
@@ -34,6 +35,8 @@ module ProMotion
34
35
  args[:resize].each { |r| attributes[:autoresizingMask] |= map_resize_symbol(r) }
35
36
  end
36
37
 
38
+ args[:left] = args.delete(:x) if args[:x]
39
+ args[:top] = args.delete(:y) if args[:y]
37
40
  if [:left, :top, :width, :height].select{ |a| args[a] && args[a] != :auto }.length == 4
38
41
  attributes[:frame] = CGRectMake(args[:left], args[:top], args[:width], args[:height])
39
42
  end
@@ -64,33 +67,35 @@ module ProMotion
64
67
  end
65
68
  nil
66
69
  end
67
-
70
+
68
71
  def add(element, attrs = {})
69
72
  add_to view_or_self, element, attrs
70
73
  end
71
74
  alias :add_element :add
72
75
  alias :add_view :add
73
76
 
74
- def remove(element)
75
- element.removeFromSuperview
76
- element = nil
77
+ def remove(elements)
78
+ Array(elements).each(&:removeFromSuperview)
77
79
  end
78
80
  alias :remove_element :remove
79
81
  alias :remove_view :remove
80
82
 
81
- def add_to(parent_element, element, attrs = {})
82
- parent_element.addSubview element
83
- if attrs && attrs.length > 0
84
- set_attributes(element, attrs)
85
- set_easy_attributes(parent_element, element, attrs)
83
+ def add_to(parent_element, elements, attrs = {})
84
+ attrs = get_attributes_from_symbol(attrs)
85
+ Array(elements).each do |element|
86
+ parent_element.addSubview element
87
+ if attrs && attrs.length > 0
88
+ set_attributes(element, attrs)
89
+ set_easy_attributes(parent_element, element, attrs)
90
+ end
86
91
  end
87
- element
92
+ elements
88
93
  end
89
-
94
+
90
95
  def view_or_self
91
96
  self.respond_to?(:view) ? self.view : self
92
97
  end
93
-
98
+
94
99
  # These three color methods are stolen from BubbleWrap.
95
100
  def rgb_color(r,g,b)
96
101
  rgba_color(r,g,b,1)
@@ -103,7 +108,7 @@ module ProMotion
103
108
 
104
109
  def hex_color(str)
105
110
  hex_color = str.gsub("#", "")
106
- case hex_color.size
111
+ case hex_color.size
107
112
  when 3
108
113
  colors = hex_color.scan(%r{[0-9A-Fa-f]}).map{ |el| (el * 2).to_i(16) }
109
114
  when 6
@@ -111,16 +116,24 @@ module ProMotion
111
116
  else
112
117
  raise ArgumentError
113
118
  end
114
-
119
+
115
120
  if colors.size == 3
116
121
  rgb_color(colors[0], colors[1], colors[2])
117
122
  else
118
123
  raise ArgumentError
119
- end
124
+ end
120
125
  end
121
-
126
+
122
127
  protected
123
-
128
+
129
+ def get_attributes_from_symbol(attrs)
130
+ return attrs if attrs.is_a?(Hash)
131
+ PM.logger.error "#{attrs} styling method is not defined" unless self.respond_to?(attrs)
132
+ new_attrs = send(attrs)
133
+ PM.logger.error "#{attrs} should return a hash" unless new_attrs.is_a?(Hash)
134
+ new_attrs
135
+ end
136
+
124
137
  def map_resize_symbol(symbol)
125
138
  @_resize_symbols ||= {
126
139
  left: UIViewAutoresizingFlexibleLeftMargin,
@@ -128,10 +141,10 @@ module ProMotion
128
141
  top: UIViewAutoresizingFlexibleTopMargin,
129
142
  bottom: UIViewAutoresizingFlexibleBottomMargin,
130
143
  width: UIViewAutoresizingFlexibleWidth,
131
- height: UIViewAutoresizingFlexibleHeight
144
+ height: UIViewAutoresizingFlexibleHeight
132
145
  }
133
146
  @_resize_symbols[symbol] || symbol
134
147
  end
135
-
148
+
136
149
  end
137
150
  end