motion-prime 0.3.2 → 0.3.3

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 (59) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +3 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +10 -0
  5. data/doc/code/getting_started.rb +61 -0
  6. data/doc/docs/docco.css +500 -0
  7. data/doc/docs/getting_started.html +177 -0
  8. data/doc/docs/public/fonts/aller-bold.eot +0 -0
  9. data/doc/docs/public/fonts/aller-bold.ttf +0 -0
  10. data/doc/docs/public/fonts/aller-bold.woff +0 -0
  11. data/doc/docs/public/fonts/aller-light.eot +0 -0
  12. data/doc/docs/public/fonts/aller-light.ttf +0 -0
  13. data/doc/docs/public/fonts/aller-light.woff +0 -0
  14. data/doc/docs/public/fonts/novecento-bold.eot +0 -0
  15. data/doc/docs/public/fonts/novecento-bold.ttf +0 -0
  16. data/doc/docs/public/fonts/novecento-bold.woff +0 -0
  17. data/doc/docs/public/stylesheets/normalize.css +375 -0
  18. data/files/app/sections/sidebar/action.rb +1 -1
  19. data/motion-prime/app_delegate.rb +8 -2
  20. data/motion-prime/elements/_content_padding_mixin.rb +12 -12
  21. data/motion-prime/elements/_content_text_mixin.rb +65 -0
  22. data/motion-prime/elements/base.rb +51 -19
  23. data/motion-prime/elements/button.rb +1 -1
  24. data/motion-prime/elements/draw.rb +26 -113
  25. data/motion-prime/elements/draw/_draw_background_mixin.rb +26 -0
  26. data/motion-prime/elements/draw/image.rb +10 -1
  27. data/motion-prime/elements/draw/label.rb +61 -42
  28. data/motion-prime/elements/draw/view.rb +14 -0
  29. data/motion-prime/elements/error_message.rb +1 -1
  30. data/motion-prime/elements/label.rb +1 -1
  31. data/motion-prime/elements/text_field.rb +2 -2
  32. data/motion-prime/elements/text_view.rb +3 -0
  33. data/motion-prime/helpers/has_style_chain_builder.rb +1 -1
  34. data/motion-prime/helpers/has_styles.rb +28 -0
  35. data/motion-prime/models/bag.rb +1 -1
  36. data/motion-prime/models/sync.rb +4 -4
  37. data/motion-prime/screens/_base_mixin.rb +1 -1
  38. data/motion-prime/screens/extensions/_navigation_bar_mixin.rb +7 -0
  39. data/motion-prime/screens/sidebar_container_screen.rb +8 -2
  40. data/motion-prime/sections/_cell_section_mixin.rb +25 -0
  41. data/motion-prime/sections/base.rb +17 -16
  42. data/motion-prime/sections/draw.rb +32 -10
  43. data/motion-prime/sections/form.rb +27 -17
  44. data/motion-prime/sections/form/base_field_section.rb +0 -1
  45. data/motion-prime/sections/form/base_header_section.rb +3 -2
  46. data/motion-prime/sections/form/password_field_section.rb +1 -1
  47. data/motion-prime/sections/table.rb +31 -10
  48. data/motion-prime/sections/table/base_cell_section.rb +1 -22
  49. data/motion-prime/sections/table/draw_cell_section.rb +5 -0
  50. data/motion-prime/styles/form.rb +12 -4
  51. data/motion-prime/support/_key_value_store.rb +0 -2
  52. data/motion-prime/support/dm_text_field.rb +2 -2
  53. data/motion-prime/support/tab_bar_controller.rb +28 -0
  54. data/motion-prime/version.rb +1 -1
  55. data/motion-prime/views/_frame_calculator_mixin.rb +75 -0
  56. data/motion-prime/views/view_styler.rb +36 -94
  57. metadata +23 -4
  58. data/motion-prime/elements/_field_dimensions_mixin.rb +0 -59
  59. data/motion-prime/elements/_text_dimensions_mixin.rb +0 -35
@@ -1,66 +1,85 @@
1
1
  motion_require '../draw.rb'
2
2
  module MotionPrime
3
3
  class LabelDrawElement < DrawElement
4
- include MotionPrime::ElementTextDimensionsMixin
5
- include MotionPrime::ElementContentPaddingMixin
4
+ include ElementContentTextMixin
5
+ include DrawBackgroundMixin
6
+
7
+ def default_padding_for(side)
8
+ return super unless side.to_s == 'top'
9
+ @padding_top || 0
10
+ end
6
11
 
7
12
  def draw_in(rect)
13
+ return if computed_options[:hidden]
14
+ size_to_fit_if_needed or set_text_position
8
15
  options = computed_options
9
- return if options[:hidden]
10
16
 
11
- size_to_fit_if_needed
17
+ # render background and border
18
+ background_rect = CGRectMake(computed_left, computed_top, computed_outer_width, computed_outer_height)
19
+ draw_background_in(background_rect, options)
12
20
 
13
- # render background
14
- bg_color = options[:background_color]
15
- if bg_color
16
- rect = CGRectMake(
17
- computed_left, computed_top, computed_width, computed_height
18
- )
21
+ # render text
22
+ color = (options[:text_color] || :black).uicolor
23
+ font = (options[:font] || :system).uifont
24
+ alignment = (options.has_key?(:text_alignment) ? options[:text_alignment] : :left).uitextalignment
25
+ line_break_mode = (options.has_key?(:line_break_mode) ? options[:line_break_mode] : :wordwrap).uilinebreakmode
26
+ label_text = options[:text].to_s
19
27
 
20
- if computed_options[:layer] && radius = options[:layer][:corner_radius]
21
- bezierPath = UIBezierPath.bezierPathWithRoundedRect rect, cornerRadius: radius
22
- context = UIGraphicsGetCurrentContext()
23
- CGContextSetStrokeColorWithColor(context, bg_color.uicolor.cgcolor)
24
- CGContextSetFillColorWithColor(context, bg_color.uicolor.cgcolor)
25
- bezierPath.stroke
26
- bezierPath.fill
27
- else
28
- bg_color.uicolor.setFill
29
- UIRectFill(rect)
30
- end
28
+ top_left_corner = CGPointMake(computed_inner_left, computed_inner_top)
29
+ if options[:number_of_lines].to_i.zero?
30
+ rect = CGRectMake(*top_left_corner.to_a, computed_width, computed_height)
31
31
  end
32
32
 
33
- # render text
34
- color = options[:text_color] || :black
35
- color.uicolor.set
36
- font = options[:font] || :system
37
- if options[:number_of_lines] != 0
38
- options[:text].to_s.drawAtPoint(
39
- CGPointMake(computed_inner_left, computed_inner_top),
40
- withFont: font.uifont
41
- )
33
+ if options[:line_spacing] || options[:underline]
34
+ # attributed string
35
+ paragrahStyle = NSMutableParagraphStyle.alloc.init
36
+
37
+ paragrahStyle.setLineSpacing(options[:line_spacing]) if options[:line_spacing]
38
+ paragrahStyle.setAlignment(alignment)
39
+ paragrahStyle.setLineBreakMode(line_break_mode)
40
+ attributes = {}
41
+ attributes[NSParagraphStyleAttributeName] = paragrahStyle
42
+ attributes[NSForegroundColorAttributeName] = color
43
+ attributes[NSFontAttributeName] = font
44
+
45
+ label_text = NSMutableAttributedString.alloc.initWithString(label_text, attributes: attributes)
46
+ if underline_range = options[:underline]
47
+ # FIXME
48
+ # label_text = NSMutableAttributedString.alloc.initWithAttributedString(label_text)
49
+ # label_text.addAttributes({NSUnderlineStyleAttributeName => NSUnderlineStyleSingle}, range: underline_range)
50
+ end
51
+
52
+ rect ? label_text.drawInRect(rect) : label_text.drawAtPoint(top_left_corner)
42
53
  else
43
- rect = CGRectMake(
44
- computed_inner_left, computed_inner_top,
45
- computed_width, computed_height
46
- )
47
- line_break = options.has_key?(:line_break_mode) ? options[:line_break_mode] : :wordwrap
48
- alignment = options.has_key?(:text_alignment) ? options[:text_alignment] : :left
49
- options[:text].to_s.drawInRect(
50
- rect, withFont: font.uifont,
51
- lineBreakMode: line_break.uilinebreakmode,
52
- alignment: alignment.uitextalignment
53
- )
54
+ # regular string
55
+ color.set
56
+ if rect
57
+ label_text.drawInRect(rect,
58
+ withFont: font,
59
+ lineBreakMode: line_break_mode,
60
+ alignment: alignment)
61
+ else
62
+ label_text.drawAtPoint(top_left_corner, withFont: font)
63
+ end
54
64
  end
55
65
  end
56
66
 
57
67
  def size_to_fit_if_needed
58
68
  if computed_options[:size_to_fit]
59
- @computed_options[:width] = content_outer_width
69
+ @computed_options[:width] ||= content_outer_width
60
70
  if computed_options[:width]
61
71
  @computed_options[:height] = content_outer_height
62
72
  end
63
73
  reset_computed_values
74
+ true
75
+ end
76
+ end
77
+
78
+ def set_text_position
79
+ if computed_options.slice(:padding_top, :padding_bottom, :padding).none?
80
+ computed_options[:width] ||= computed_width
81
+ @padding_top = (computed_outer_height - content_height)/2
82
+ # @padding_top += 1 unless @padding_top.zero?
64
83
  end
65
84
  end
66
85
  end
@@ -0,0 +1,14 @@
1
+ motion_require '../draw.rb'
2
+ module MotionPrime
3
+ class ViewDrawElement < DrawElement
4
+ include DrawBackgroundMixin
5
+
6
+ def draw_in(rect)
7
+ return if computed_options[:hidden]
8
+ options = computed_options
9
+
10
+ background_rect = CGRectMake(computed_left, computed_top, computed_outer_width, computed_outer_height)
11
+ draw_background_in(background_rect, options)
12
+ end
13
+ end
14
+ end
@@ -2,7 +2,7 @@ motion_require './label'
2
2
  module MotionPrime
3
3
  class ErrorMessageElement < LabelElement
4
4
  include MotionPrime::ElementContentPaddingMixin
5
- include MotionPrime::ElementTextDimensionsMixin
5
+ include MotionPrime::ElementContentTextMixin
6
6
 
7
7
  def view_class
8
8
  "MPLabel"
@@ -1,7 +1,7 @@
1
1
  module MotionPrime
2
2
  class LabelElement < BaseElement
3
3
  include MotionPrime::ElementContentPaddingMixin
4
- include MotionPrime::ElementTextDimensionsMixin
4
+ include MotionPrime::ElementContentTextMixin
5
5
 
6
6
  before_render :size_to_fit_if_needed
7
7
  after_render :size_to_fit
@@ -1,8 +1,8 @@
1
1
  module MotionPrime
2
2
  class TextFieldElement < BaseElement
3
3
  include MotionPrime::ElementContentPaddingMixin
4
- include MotionPrime::ElementFieldDimensionsMixin
5
-
4
+ include MotionPrime::ElementContentTextMixin
5
+
6
6
  def view_class
7
7
  "DMTextField"
8
8
  end
@@ -1,5 +1,8 @@
1
1
  module MotionPrime
2
2
  class TextViewElement < BaseElement
3
+ include MotionPrime::ElementContentPaddingMixin
4
+ include MotionPrime::ElementContentTextMixin
5
+
3
6
  def view_class
4
7
  "DMTextView"
5
8
  end
@@ -1,7 +1,7 @@
1
1
  module MotionPrime
2
2
  module HasStyleChainBuilder
3
3
  def build_styles_chain(base_styles, suffixes)
4
- [*base_styles].uniq.compact.map { |base_style| [*suffixes].uniq.compact.map { |suffix| [base_style, suffix].join('_').to_sym } }.flatten
4
+ [*base_styles].compact.map(&:to_s).uniq.map { |base_style| [*suffixes].compact.map(&:to_s).uniq.map { |suffix| [base_style, suffix].join('_').to_sym } }.flatten
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,28 @@
1
+ module MotionPrime
2
+ module HasStyles
3
+ def prepare_gradient(options)
4
+ colors = options[:colors].map(&:uicolor).map(&:cgcolor)
5
+ locations = options[:locations] if options[:locations]
6
+
7
+ if self.is_a?(DrawSection)
8
+ color_space = CGColorSpaceCreateDeviceRGB()
9
+ locations_pointer = Pointer.new(:float, 2)
10
+ locations.each_with_index { |loc, id| locations_pointer[id] = loc }
11
+ gradient = CGGradientCreateWithColors(color_space, colors, locations_pointer)
12
+ # CGColorSpaceRelease(color_space)
13
+ else
14
+ gradient = CAGradientLayer.layer
15
+
16
+ gradient.frame = if options[:frame_width]
17
+ CGRectMake(options[:frame_x].to_f, options[:frame_y].to_f, options[:frame_width].to_f, options[:frame_height].to_f)
18
+ else
19
+ options[:parent_frame] || CGRectZero
20
+ end
21
+
22
+ gradient.colors = colors
23
+ gradient.locations = locations
24
+ end
25
+ gradient
26
+ end
27
+ end
28
+ end
@@ -50,7 +50,7 @@ module MotionPrime
50
50
  object.map { |entity| prepare_for_store(entity) }.compact
51
51
  else
52
52
  object.bag_key = self.key
53
- if object.id.present? && self.store && self.find(id: object.id).any?
53
+ if object.id.present? && self.store && self.find(id: object.id, bag_key: self.key).any?
54
54
  raise StoreError, "duplicated item added `#{object.class_name_without_kvo}` with `id` = #{object.id}"
55
55
  end
56
56
  object
@@ -38,6 +38,9 @@ module MotionPrime
38
38
  should_fetch
39
39
  end
40
40
 
41
+ should_fetch = persisted? if should_fetch.nil?
42
+ should_update ||= new_record? unless should_fetch
43
+
41
44
  method = sync_options[:method]
42
45
  method ||= if should_update
43
46
  persisted? ? :put : :post
@@ -51,9 +54,6 @@ module MotionPrime
51
54
  should_update = false
52
55
  end
53
56
 
54
- should_fetch = !new_record? if should_fetch.nil?
55
- should_update ||= new_record? unless should_fetch
56
-
57
57
  fetch_with_url url do |data, status_code|
58
58
  save if sync_options[:save]
59
59
  block.call(data, status_code) if use_callback
@@ -155,7 +155,7 @@ module MotionPrime
155
155
  use_callback = block_given?
156
156
  puts "SYNC: started sync for #{key} in #{self.class_name_without_kvo}"
157
157
  api_client.get normalize_sync_url(options[:sync_url]) do |response, status_code|
158
- data = options.has_key?(:sync_key) ? response[options[:sync_key]] : response
158
+ data = options[:sync_key] && response ? response[options[:sync_key]] : response
159
159
  if data
160
160
  # Update/Create existing records
161
161
  data.each do |attributes|
@@ -10,7 +10,7 @@ module MotionPrime
10
10
  include MotionPrime::ScreenOrientationsMixin
11
11
  include MotionPrime::ScreenNavigationMixin
12
12
 
13
- attr_accessor :parent_screen, :modal, :params, :main_section, :options
13
+ attr_accessor :parent_screen, :modal, :params, :main_section, :options, :tab_bar
14
14
  class_attribute :current_screen
15
15
 
16
16
  included do
@@ -32,6 +32,13 @@ module MotionPrime
32
32
  end
33
33
  end
34
34
 
35
+ def set_navigation_right_image(args = {})
36
+ url = args.delete(:url)
37
+ view = add_view(UIImageView, args)
38
+ view.setImageWithURL NSURL.URLWithString(url), placeholderImage: nil
39
+ navigationItem.rightBarButtonItem = UIBarButtonItem.alloc.initWithCustomView(view)
40
+ end
41
+
35
42
  def create_navigation_button(title, args = {})
36
43
  args[:style] ||= UIBarButtonItemStylePlain
37
44
  args[:target] ||= self
@@ -39,7 +39,7 @@ module MotionPrime
39
39
 
40
40
  def content_controller=(c)
41
41
  controller = prepare_controller(c)
42
- if content_controller.nil?
42
+ if should_reinit_content?(controller)
43
43
  self.setContentViewController controller
44
44
  else
45
45
  content_controller.viewControllers = [controller]
@@ -57,9 +57,15 @@ module MotionPrime
57
57
 
58
58
  private
59
59
 
60
+ def should_reinit_content?(new_controller)
61
+ content_controller.nil? ||
62
+ content_controller.is_a?(TabBarController) ||
63
+ new_controller.is_a?(TabBarController)
64
+ end
65
+
60
66
  def prepare_controller(controller)
61
67
  controller = setup_screen_for_open(controller, {})
62
- if content_controller.nil?
68
+ if should_reinit_content?(controller)
63
69
  controller.wrap_in_navigation if controller.respond_to?(:wrap_in_navigation)
64
70
  controller.send(:on_screen_load) if controller.respond_to?(:on_screen_load)
65
71
  controller = controller.main_controller if controller.respond_to?(:main_controller)
@@ -0,0 +1,25 @@
1
+ module MotionPrime
2
+ module CellSectionMixin
3
+ attr_writer :table
4
+
5
+ def table
6
+ @table ||= options[:table]
7
+ end
8
+
9
+ def section_styles
10
+ @section_styles ||= table.try(:cell_styles, self) || {}
11
+ end
12
+
13
+ def cell_type
14
+ @cell_type ||= begin
15
+ self.is_a?(BaseFieldSection) ? :field : :cell
16
+ end
17
+ end
18
+
19
+ def cell_name
20
+ return name unless table
21
+ table_name = table.name.gsub('_table', '')
22
+ name.gsub("#{table_name}_", '')
23
+ end
24
+ end
25
+ end
@@ -23,6 +23,7 @@ module MotionPrime
23
23
  define_callbacks :render
24
24
 
25
25
  def initialize(options = {})
26
+ super
26
27
  @options = options
27
28
  @model = options[:model]
28
29
  @name = options[:name] ||= default_name
@@ -39,17 +40,25 @@ module MotionPrime
39
40
  end
40
41
 
41
42
  def container_options
42
- @normalized_container_options or begin
43
- # priority: class; from styles; passed directly
44
- raw_container_options = self.class.container_options.try(:clone) || {}
45
- passed_container_options = options.delete(:container) || {}
46
- raw_container_options.merge!(passed_container_options)
47
- @normalized_container_options = normalize_options(raw_container_options)
48
- style_container_options = style_options.delete(:container) || {}
49
- @normalized_container_options.merge!(style_container_options.except(*passed_container_options.keys))
43
+ @container_options ||= (style_options.delete(:container) || {}).merge(base_container_options)
44
+ end
45
+
46
+ def base_container_options
47
+ @base_container_options ||= begin
48
+ container_options = self.class.container_options.try(:clone) || {}
49
+ container_options.merge!(options.delete(:container) || {})
50
+ normalize_options(container_options)
50
51
  end
51
52
  end
52
53
 
54
+ def container_height
55
+ container_options[:height] || DEFAULT_CONTENT_HEIGHT
56
+ end
57
+
58
+ def container_styles
59
+ base_container_options[:styles]
60
+ end
61
+
53
62
  def default_name
54
63
  self.class_name_without_kvo.demodulize.underscore.gsub(/\_section$/, '')
55
64
  end
@@ -142,14 +151,6 @@ module MotionPrime
142
151
  end
143
152
  end
144
153
 
145
- def container_height
146
- container_options[:height] || DEFAULT_CONTENT_HEIGHT
147
- end
148
-
149
- def container_styles
150
- container_options[:styles]
151
- end
152
-
153
154
  def on_keyboard_show; end
154
155
  def on_keyboard_hide; end
155
156
  def keyboard_will_show; end
@@ -10,6 +10,7 @@ module MotionPrime
10
10
  # element :avatar, type: :image, image: 'defaults/avatar.jpg'
11
11
  # end
12
12
  #
13
+ include HasStyles
13
14
 
14
15
  attr_accessor :container_view
15
16
 
@@ -25,19 +26,21 @@ module MotionPrime
25
26
  end
26
27
 
27
28
  def render!
29
+ options = view_style_options
30
+ options.merge!(section: WeakRef.new(self), background_color: :clear)
31
+
28
32
  if container_options[:as].to_s == 'cell'
29
- @container_view = screen.add_view DMCellWithSection, {
30
- section: self, styles: container_options[:styles],
31
- reuse_identifier: container_options[:reuse_identifier]
32
- }
33
+ @container_view = screen.add_view DMCellWithSection, options.merge({
34
+ reuse_identifier: container_options[:reuse_identifier],
35
+ parent_view: (table if respond_to?(:table))
36
+ })
33
37
  else
34
- @container_view = screen.add_view DMViewWithSection, {
35
- section: self, styles: container_options[:styles],
36
- width: container_options[:width] || 320,
38
+ @container_view = screen.add_view DMViewWithSection, options.merge({
39
+ width: container_options[:width] || 320, # TODO: remove these options (use styles)
37
40
  height: container_options[:height] || 100,
38
41
  top: container_options[:top] || 0,
39
42
  left: container_options[:left] || 0
40
- }
43
+ })
41
44
  end
42
45
  end
43
46
 
@@ -61,9 +64,28 @@ module MotionPrime
61
64
  end
62
65
  end
63
66
 
67
+ def view_style_options
68
+ @view_style_options ||= begin
69
+ options = Styles.for(container_options[:styles])
70
+ normalize_options(options)
71
+ options
72
+ end
73
+ end
74
+
64
75
  def draw_background(rect)
65
- if container_options[:background_color]
66
- container_options[:background_color].uicolor.setFill
76
+ if gradient_options = view_style_options[:gradient]
77
+ start_point = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect))
78
+ end_point = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect))
79
+
80
+ context = UIGraphicsGetCurrentContext()
81
+ # CGContextSaveGState(context)
82
+ CGContextAddRect(context, rect)
83
+ CGContextClip(context)
84
+ gradient = prepare_gradient(gradient_options)
85
+ CGContextDrawLinearGradient(context, gradient, start_point, end_point, 0)
86
+ # CGContextRestoreGState(context)
87
+ elsif background_color = (container_options[:background_color] || view_style_options[:background_color])
88
+ background_color.uicolor.setFill
67
89
  UIRectFill(rect)
68
90
  end
69
91
  end