motion-prime 0.7.2 → 0.8.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 (60) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +7 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +8 -9
  5. data/ROADMAP.md +4 -5
  6. data/bin/prime +19 -14
  7. data/files/Rakefile +0 -1
  8. data/files/app/app_delegate.rb +0 -12
  9. data/files/app/config/base.rb +1 -12
  10. data/files/app/screens/application.rb +1 -5
  11. data/files/app/sections/.gitkeep +0 -0
  12. data/files/app/styles/.gitkeep +0 -0
  13. data/files/resources/Default-568h@2x.png +0 -0
  14. data/files/resources/Default.png +0 -0
  15. data/files/resources/Default@2x.png +0 -0
  16. data/motion-prime/api_client.rb +2 -1
  17. data/motion-prime/config/base.rb +8 -3
  18. data/motion-prime/config/config.rb +18 -0
  19. data/motion-prime/elements/_text_mixin.rb +0 -4
  20. data/motion-prime/elements/base_element.rb +64 -51
  21. data/motion-prime/elements/draw/image.rb +3 -2
  22. data/motion-prime/elements/draw/label.rb +9 -2
  23. data/motion-prime/helpers/has_search_bar.rb +10 -1
  24. data/motion-prime/screens/_base_mixin.rb +1 -1
  25. data/motion-prime/screens/_sections_mixin.rb +8 -4
  26. data/motion-prime/sections/__section_with_container_mixin.rb +52 -0
  27. data/motion-prime/sections/_cell_section_mixin.rb +7 -21
  28. data/motion-prime/sections/_delegate_mixin.rb +12 -0
  29. data/motion-prime/sections/_draw_section_mixin.rb +4 -21
  30. data/motion-prime/sections/base_section.rb +7 -7
  31. data/motion-prime/sections/form.rb +7 -0
  32. data/motion-prime/sections/form/base_field_section.rb +9 -0
  33. data/motion-prime/sections/form/date_field_section.rb +1 -1
  34. data/motion-prime/sections/form/form_delegate.rb +0 -1
  35. data/motion-prime/sections/form/password_field_section.rb +1 -1
  36. data/motion-prime/sections/form/select_field_section.rb +1 -1
  37. data/motion-prime/sections/form/string_field_section.rb +1 -1
  38. data/motion-prime/sections/form/submit_field_section.rb +1 -1
  39. data/motion-prime/sections/form/switch_field_section.rb +1 -1
  40. data/motion-prime/sections/form/text_field_section.rb +1 -1
  41. data/motion-prime/sections/tabbed.rb +7 -0
  42. data/motion-prime/sections/table.rb +20 -7
  43. data/motion-prime/sections/table/table_delegate.rb +1 -9
  44. data/motion-prime/styles/base.rb +1 -1
  45. data/motion-prime/styles/form.rb +7 -7
  46. data/motion-prime/version.rb +1 -1
  47. data/motion-prime/views/layout.rb +4 -2
  48. metadata +6 -14
  49. data/files/app/screens/help.rb +0 -6
  50. data/files/app/screens/home.rb +0 -8
  51. data/files/app/screens/sidebar.rb +0 -14
  52. data/files/app/sections/home/section.rb +0 -3
  53. data/files/app/sections/sidebar/action.rb +0 -5
  54. data/files/app/sections/sidebar/table.rb +0 -20
  55. data/files/app/styles/home.rb +0 -9
  56. data/files/resources/fonts/ubuntu.ttf +0 -0
  57. data/files/resources/images/arrow.png +0 -0
  58. data/files/resources/images/menu_button.png +0 -0
  59. data/files/resources/images/menu_button@2x.png +0 -0
  60. data/motion-prime/styles/sidebar.rb +0 -23
@@ -66,14 +66,15 @@ module MotionPrime
66
66
 
67
67
  def load_image
68
68
  return if image_data || !computed_options[:url]
69
+ # TODO: why so many references?
70
+ @strong_refs = section.strong_references + [screen.main_controller.strong_ref]
69
71
  BW::Reactor.schedule do
70
72
  manager = SDWebImageManager.sharedManager
71
- @strong_refs = section.strong_references
72
73
  manager.downloadWithURL(computed_options[:url],
73
74
  options: 0,
74
75
  progress: lambda{ |r_size, e_size| },
75
76
  completed: lambda{ |image, error, type, finished|
76
- if !image || screen.retainCount == 1 || section.retainCount == 1
77
+ if !image || screen.main_controller.retainCount == 1 || section.retainCount == 1
77
78
  @strong_refs = nil
78
79
  return
79
80
  end
@@ -41,6 +41,8 @@ module MotionPrime
41
41
  draw_in_context(UIGraphicsGetCurrentContext())
42
42
  end
43
43
 
44
+ # using hack for bug described here: http://stackoverflow.com/questions/19232850/nsattributedstring-drawinrect-disappears-when-the-frame-is-offset
45
+ # TODO: check it in iOS 7.1 and remove CGContext manuplations (pass innerRect/topLeftCorner) if fixed
44
46
  def draw_in_context(context)
45
47
  return if computed_options[:hidden]
46
48
  size_to_fit_if_needed
@@ -53,11 +55,16 @@ module MotionPrime
53
55
  if options[:is_html] || options[:line_spacing] || options[:line_height] || options[:underline]
54
56
  prepared_text = options[:is_html] ? html_string(options) : attributed_string(options)
55
57
 
58
+ CGContextSaveGState(context)
56
59
  if options[:inner_rect]
57
- prepared_text.drawInRect(options[:inner_rect])
60
+ rect = options[:inner_rect]
61
+ CGContextTranslateCTM(context, *rect.origin.to_a)
62
+ prepared_text.drawInRect(CGRectMake(0, 0, *rect.size.to_a))
58
63
  else
59
- prepared_text.drawAtPoint(options[:top_left_corner])
64
+ CGContextTranslateCTM(context, *options[:top_left_corner].to_a)
65
+ prepared_text.drawAtPoint(CGPointMake(0, 0))
60
66
  end
67
+ CGContextRestoreGState(context)
61
68
  else
62
69
  # regular string
63
70
  prepared_text = options[:text]
@@ -2,6 +2,7 @@
2
2
  module MotionPrime
3
3
  module HasSearchBar
4
4
  def add_search_bar(options = {}, &block)
5
+ @_search_timeout = options.delete(:timeout)
5
6
  target = options.delete(:target)
6
7
 
7
8
  @_search_bar = create_search_bar(options)
@@ -20,6 +21,7 @@ module MotionPrime
20
21
  end
21
22
 
22
23
  def dealloc
24
+ BW::Reactor.cancel_timer(@_search_timer) if @_search_timer
23
25
  @_search_bar.try(:setDelegate, nil)
24
26
  @_search_bar = nil
25
27
  super
@@ -35,10 +37,17 @@ module MotionPrime
35
37
  end
36
38
 
37
39
  def searchBar(search_bar, textDidChange: text)
38
- @search_callback.call(text)
40
+ BW::Reactor.cancel_timer(@_search_timer) if @_search_timer
41
+ if @_search_timeout
42
+ @_search_timer = BW::Reactor.add_timer(@_search_timeout.to_f/1000, proc{ @search_callback.call(text) }.weak!)
43
+ else
44
+ @search_callback.call(text)
45
+ end
39
46
  end
40
47
 
41
48
  def searchBarSearchButtonClicked(search_bar)
49
+ BW::Reactor.cancel_timer(@_search_timer) if @_search_timer
50
+ @search_callback.call(search_bar.text)
42
51
  search_bar.resignFirstResponder
43
52
  end
44
53
  end
@@ -12,7 +12,7 @@ module MotionPrime
12
12
  include MotionPrime::ScreenNavigationMixin
13
13
  include MotionPrime::ScreenSectionsMixin
14
14
 
15
- attr_accessor :parent_screen, :modal, :params, :main_section, :options, :tab_bar
15
+ attr_accessor :parent_screen, :modal, :params, :options, :tab_bar
16
16
  class_attribute :current_screen
17
17
 
18
18
  def app_delegate
@@ -14,6 +14,10 @@ module MotionPrime
14
14
  render_sections
15
15
  end
16
16
 
17
+ def all_sections
18
+ @sections.values
19
+ end
20
+
17
21
  def create_sections
18
22
  section_options = self.class._section_options
19
23
  return unless section_options
@@ -31,16 +35,16 @@ module MotionPrime
31
35
 
32
36
  def render_sections
33
37
  return unless @sections
34
- if @sections.count > 1
35
- @main_section = MotionPrime::TableSection.new(model: @sections.values, screen: self)
38
+ if all_sections.count > 1
39
+ @main_section = MotionPrime::TableSection.new(model: all_sections, screen: self)
36
40
  @main_section.render
37
41
  else
38
- @sections.first.render
42
+ all_sections.first.render
39
43
  end
40
44
  end
41
45
 
42
46
  def main_section
43
- @main_section || @sections.first
47
+ @main_section || all_sections.first
44
48
  end
45
49
 
46
50
  module ClassMethods
@@ -0,0 +1,52 @@
1
+ module MotionPrime
2
+ module SectionWithContainerMixin
3
+ extend ::MotionSupport::Concern
4
+
5
+ included do
6
+ class_attribute :container_element_options
7
+ end
8
+
9
+ def container_view
10
+ container_element.try(:view)
11
+ end
12
+
13
+ def init_container_element(options = {})
14
+ @container_element ||= begin
15
+ options.merge!({
16
+ screen: screen,
17
+ section: self.weak_ref,
18
+ has_drawn_content: true
19
+ })
20
+ container_element_options = self.class.container_element_options.clone
21
+ type = container_element_options.delete(:type)
22
+ options.merge!(container_element_options)
23
+ MotionPrime::BaseElement.factory(type, options)
24
+ end
25
+ end
26
+
27
+ def load_container_with_elements(options = {})
28
+ init_container_element(options[:container] || {})
29
+ @container_element.compute_options! unless @container_element.computed_options
30
+ compute_element_options(options[:elements] || {})
31
+
32
+ if respond_to?(:prerender_elements_for_state) && prerender_enabled?
33
+ prerender_elements_for_state(:normal)
34
+ end
35
+ end
36
+
37
+ private
38
+ def compute_element_options(options = {})
39
+ self.elements.values.each do |element|
40
+ element.size_to_fit_if_needed if element.is_a?(LabelDrawElement)
41
+ element.compute_options! if element.respond_to?(:computed_options) && !element.computed_options
42
+ end
43
+ end
44
+
45
+
46
+ module ClassMethods
47
+ def container_element(options)
48
+ self.container_element_options = options
49
+ end
50
+ end
51
+ end
52
+ end
@@ -3,11 +3,14 @@ module MotionPrime
3
3
  module CellSectionMixin
4
4
  extend ::MotionSupport::Concern
5
5
 
6
+ include SectionWithContainerMixin
7
+
6
8
  attr_writer :table
7
9
  attr_reader :pending_display
8
10
 
9
11
  included do
10
12
  class_attribute :custom_cell_name
13
+ container_element type: :table_view_cell
11
14
  end
12
15
 
13
16
  def table
@@ -42,27 +45,10 @@ module MotionPrime
42
45
  end
43
46
 
44
47
  def init_container_element(options = {})
45
- @container_element ||= begin
46
- options.merge!({
47
- screen: screen,
48
- section: self.weak_ref,
49
- has_drawn_content: true
50
- })
51
- options[:styles] ||= []
52
- options[:styles] = [:"#{table.name}_first_cell"] if table.data.first == self
53
- options[:styles] = [:"#{table.name}_last_cell"] if table.data.last == self
54
- MotionPrime::BaseElement.factory(:table_view_cell, options)
55
- end
56
- end
57
-
58
- # FIXME: Why this duplicates functionality from other parts, e.g. draw_in?
59
- def load_container_element(options = {})
60
- init_container_element(options)
61
- load_elements
62
- @container_element.compute_options! unless @container_element.computed_options
63
- if respond_to?(:prerender_elements_for_state) && prerender_enabled?
64
- prerender_elements_for_state(:normal)
65
- end
48
+ options[:styles] ||= []
49
+ options[:styles] = [:"#{table.name}_first_cell"] if table.data.first == self
50
+ options[:styles] = [:"#{table.name}_last_cell"] if table.data.last == self
51
+ super(options)
66
52
  end
67
53
 
68
54
  def pending_display!
@@ -0,0 +1,12 @@
1
+ module MotionPrime
2
+ module DelegateMixin
3
+ def delegated_by(view)
4
+ @delegated_views ||= []
5
+ @delegated_views << view
6
+ end
7
+
8
+ def clear_delegated
9
+ Array.wrap(@delegated_views).each { |view| view.setDelegate(nil) }
10
+ end
11
+ end
12
+ end
@@ -4,30 +4,13 @@ module MotionPrime
4
4
 
5
5
  include HasStyles
6
6
  include FrameCalculatorMixin
7
+ include SectionWithContainerMixin
8
+
7
9
  attr_accessor :container_element, :container_gesture_recognizers, :cached_draw_image
10
+
8
11
  included do
9
12
  class_attribute :prerender_enabled
10
- end
11
-
12
- def container_view
13
- container_element.try(:view)
14
- end
15
-
16
- def init_container_element(options = {})
17
- @container_element ||= begin
18
- options.merge!({
19
- screen: screen,
20
- section: self.weak_ref,
21
- has_drawn_content: true
22
- })
23
- options[:styles] ||= []
24
- MotionPrime::BaseElement.factory(:view_with_section, options)
25
- end
26
- end
27
-
28
- def load_container_element(options = {})
29
- init_container_element(options)
30
- @container_element.compute_options! unless @container_element.computed_options
13
+ container_element type: :view_with_section
31
14
  end
32
15
 
33
16
  def draw_in(rect, state = :normal)
@@ -19,6 +19,7 @@ module MotionPrime
19
19
  include HasNormalizer
20
20
  include HasClassFactory
21
21
  include DrawSectionMixin
22
+ include DelegateMixin
22
23
 
23
24
  attr_accessor :screen, :model, :name, :options, :elements, :section_styles
24
25
  class_attribute :elements_options, :container_options, :keyboard_close_bindings
@@ -145,13 +146,6 @@ module MotionPrime
145
146
  self.instance_eval(&@options_block) if @options_block.is_a?(Proc)
146
147
  end
147
148
 
148
- def load_elements
149
- self.elements.values.each do |element|
150
- element.size_to_fit_if_needed if element.is_a?(LabelDrawElement)
151
- element.compute_options! if element.respond_to?(:computed_options) && !element.computed_options
152
- end
153
- end
154
-
155
149
  def add_element(key, options = {})
156
150
  return unless render_element?(key)
157
151
  opts = options.clone
@@ -334,6 +328,12 @@ module MotionPrime
334
328
  end
335
329
 
336
330
  class << self
331
+ def inherited(subclass)
332
+ subclass.elements_options = self.elements_options.try(:clone)
333
+ subclass.container_options = self.container_options.try(:clone)
334
+ subclass.keyboard_close_bindings = self.keyboard_close_bindings.try(:clone)
335
+ end
336
+
337
337
  def element(name, options = {}, &block)
338
338
  options[:name] ||= name
339
339
  options[:type] ||= :label
@@ -210,6 +210,13 @@ module MotionPrime
210
210
  end
211
211
 
212
212
  class << self
213
+ def inherited(subclass)
214
+ super
215
+ subclass.fields_options = self.fields_options.try(:clone)
216
+ subclass.text_field_limits = self.text_field_limits.try(:clone)
217
+ subclass.text_view_limits = self.text_view_limits.try(:clone)
218
+ end
219
+
213
220
  def field(name, options = {}, &block)
214
221
  options[:name] = name
215
222
  options[:type] ||= :string
@@ -103,6 +103,15 @@ module MotionPrime
103
103
  NSLog("can't blur on element #{self.class_name_without_kvo}")
104
104
  end
105
105
 
106
+ def default_label_options
107
+ label_options = options[:label]
108
+ if label_options.has_key?(:text)
109
+ label_options
110
+ else
111
+ {text: options[:name].to_s.titleize}.merge(label_options)
112
+ end
113
+ end
114
+
106
115
  def bind_text_input
107
116
  view(:input).on :change do |view|
108
117
  focus
@@ -2,7 +2,7 @@ module MotionPrime
2
2
  class DateFieldSection < BaseFieldSection
3
3
  container height: 190
4
4
  element :label, type: :label do
5
- options[:label] || {}
5
+ default_label_options
6
6
  end
7
7
  element :date_picker, type: :date_picker do
8
8
  options[:input] || {}
@@ -22,7 +22,6 @@ module MotionPrime
22
22
  table_section.allow_string_replacement?(text_field, limit, range, string)
23
23
  end
24
24
 
25
-
26
25
  def textViewDidBeginEditing(text_view)
27
26
  table_section.on_input_edit_begin(text_view)
28
27
  end
@@ -1,7 +1,7 @@
1
1
  module MotionPrime
2
2
  class PasswordFieldSection < BaseFieldSection
3
3
  element :label, type: :label do
4
- options[:label] || {}
4
+ default_label_options
5
5
  end
6
6
  element :input, type: :text_field, delegate: proc { form.table_delegate } do
7
7
  {secure_text_entry: true}.merge(options[:input] || {})
@@ -1,7 +1,7 @@
1
1
  module MotionPrime
2
2
  class SelectFieldSection < BaseFieldSection
3
3
  element :label, type: :label do
4
- options[:label] || {}
4
+ default_label_options
5
5
  end
6
6
  element :button, type: :button do
7
7
  options[:button] || {}
@@ -1,7 +1,7 @@
1
1
  module MotionPrime
2
2
  class StringFieldSection < BaseFieldSection
3
3
  element :label, type: :label do
4
- options[:label] || {}
4
+ default_label_options
5
5
  end
6
6
 
7
7
  element :input, type: :text_field, delegate: proc { form.table_delegate } do
@@ -1,7 +1,7 @@
1
1
  module MotionPrime
2
2
  class SubmitFieldSection < BaseFieldSection
3
3
  element :submit, type: :button do
4
- {title: options[:title]}.merge(options[:button] || {})
4
+ {title: options[:name].to_s.titleize}.merge(options[:button] || {})
5
5
  end
6
6
  element :error_message, type: :error_message, text: proc { all_errors.join("\n") if observing_errors? }
7
7
 
@@ -1,7 +1,7 @@
1
1
  module MotionPrime
2
2
  class SwitchFieldSection < BaseFieldSection
3
3
  element :label, type: :label do
4
- options[:label] || {}
4
+ default_label_options
5
5
  end
6
6
  element :input, type: :switch do
7
7
  options[:input] || {}
@@ -1,7 +1,7 @@
1
1
  module MotionPrime
2
2
  class TextFieldSection < BaseFieldSection
3
3
  element :label, type: :label do
4
- options[:label] || {}
4
+ default_label_options
5
5
  end
6
6
  element :input, type: :text_view, delegate: proc { form.table_delegate } do
7
7
  {editable: true}.merge(options[:input] || {})
@@ -85,6 +85,13 @@ module MotionPrime
85
85
  end
86
86
 
87
87
  class << self
88
+ def inherited(subclass)
89
+ super
90
+ subclass.tabs_options = self.tabs_options.try(:clone)
91
+ subclass.tabs_default = self.tabs_default.try(:clone)
92
+ subclass.tabs_indexes = self.tabs_indexes.try(:clone)
93
+ end
94
+
88
95
  def tab(id, options = {})
89
96
  options[:name] ||= id.to_s.titleize
90
97
  options[:id] = id
@@ -58,6 +58,13 @@ module MotionPrime
58
58
  @preloader_queue[-1] = :cancelled if @preloader_queue.present?
59
59
  end
60
60
 
61
+ def add_cells(cells)
62
+ prepare_table_cells(cells)
63
+ @data ||= []
64
+ @data += cells
65
+ reload_table_data
66
+ end
67
+
61
68
  def reload_cell(section)
62
69
  section.elements.values.each(&:compute_options!)
63
70
  section.cached_draw_image = nil
@@ -307,7 +314,7 @@ module MotionPrime
307
314
  def load_cell_by_index(index, options = {})
308
315
  section = rows_for_section(index.section)[index.row]
309
316
  if section.load_section && options[:preload] && !section.container_element && async_data? # perform only if just loaded
310
- section.load_container_element(container_element_options_for(index))
317
+ section.load_container_with_elements(container: container_element_options_for(index))
311
318
  end
312
319
  section
313
320
  end
@@ -387,17 +394,17 @@ module MotionPrime
387
394
  @preloader_queue ||= []
388
395
  @strong_refs ||= []
389
396
 
390
- # TODO: do not release parent_objcets unless finished
391
- BW::Reactor.schedule(@preloader_queue.count) do |queue_id|
392
- @preloader_queue[queue_id] = :in_progress
393
- @strong_refs[queue_id] = screen.main_controller.strong_ref
397
+ # TODO: we do we need to keep screen ref too?
398
+ queue_id = @preloader_queue.count
399
+ @strong_refs[queue_id] = [screen.strong_ref, screen.main_controller.strong_ref]
400
+ @preloader_queue[queue_id] = :in_progress
401
+ BW::Reactor.schedule(queue_id) do |queue_id|
394
402
  result = load_count.times do |offset|
395
403
  if @preloader_queue[queue_id] == :cancelled
396
404
  @strong_refs[queue_id] = nil
397
405
  break
398
406
  end
399
-
400
- if screen.retainCount == 1
407
+ if screen.main_controller.retainCount == 1
401
408
  @strong_refs[queue_id] = nil
402
409
  @preloader_queue[queue_id] = :dealloc
403
410
  break
@@ -423,6 +430,12 @@ module MotionPrime
423
430
  end
424
431
 
425
432
  class << self
433
+ def inherited(subclass)
434
+ super
435
+ subclass.async_data_options = self.async_data_options.try(:clone)
436
+ subclass.section_header_options = self.section_header_options.try(:clone)
437
+ end
438
+
426
439
  def async_table_data(options = {})
427
440
  self.async_data_options = options
428
441
  end