motion-prime 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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