motion-prime 0.4.3 → 0.4.4

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 (47) hide show
  1. checksums.yaml +6 -14
  2. data/CHANGELOG.md +5 -0
  3. data/Gemfile.lock +1 -1
  4. data/ROADMAP.md +9 -4
  5. data/doc/code/getting_started.rb +1 -2
  6. data/doc/code/screens.rb +54 -0
  7. data/doc/docs/getting_started.html +27 -6
  8. data/doc/docs/screens.html +166 -0
  9. data/files/Gemfile +1 -1
  10. data/files/Gemfile.lock +64 -0
  11. data/files/app/environment.rb +10 -0
  12. data/files/app/styles/sidebar.rb +3 -10
  13. data/files/resources/images/menu_button.png +0 -0
  14. data/files/resources/images/menu_button@2x.png +0 -0
  15. data/motion-prime/app_delegate.rb +19 -0
  16. data/motion-prime/core_ext/kernel.rb +4 -0
  17. data/motion-prime/elements/_content_text_mixin.rb +23 -11
  18. data/motion-prime/elements/_text_mixin.rb +54 -0
  19. data/motion-prime/elements/base_element.rb +19 -14
  20. data/motion-prime/elements/draw.rb +22 -1
  21. data/motion-prime/elements/draw/_draw_background_mixin.rb +28 -28
  22. data/motion-prime/elements/draw/image.rb +67 -48
  23. data/motion-prime/elements/draw/label.rb +59 -49
  24. data/motion-prime/elements/draw/view.rb +5 -3
  25. data/motion-prime/helpers/has_style_chain_builder.rb +1 -3
  26. data/motion-prime/models/association_collection.rb +8 -0
  27. data/motion-prime/models/finder.rb +8 -0
  28. data/motion-prime/mp.rb +4 -0
  29. data/motion-prime/screens/_navigation_mixin.rb +4 -0
  30. data/motion-prime/screens/base_screen.rb +7 -0
  31. data/motion-prime/screens/sidebar_container_screen.rb +2 -2
  32. data/motion-prime/sections/_cell_section_mixin.rb +44 -5
  33. data/motion-prime/sections/_draw_section_mixin.rb +120 -0
  34. data/motion-prime/sections/base_section.rb +29 -24
  35. data/motion-prime/sections/form.rb +48 -65
  36. data/motion-prime/sections/form/base_field_section.rb +2 -2
  37. data/motion-prime/sections/table.rb +143 -82
  38. data/motion-prime/sections/table/table_delegate.rb +48 -0
  39. data/motion-prime/styles/form.rb +1 -1
  40. data/motion-prime/support/mp_cell_with_section.rb +6 -2
  41. data/motion-prime/support/mp_view_with_section.rb +1 -1
  42. data/motion-prime/version.rb +1 -1
  43. data/motion-prime/views/_frame_calculator_mixin.rb +4 -8
  44. data/motion-prime/views/layout.rb +1 -0
  45. data/motion-prime/views/view_styler.rb +3 -12
  46. metadata +34 -26
  47. data/motion-prime/sections/_draw_mixin.rb +0 -66
@@ -2,82 +2,92 @@ motion_require '../draw.rb'
2
2
  module MotionPrime
3
3
  class LabelDrawElement < DrawElement
4
4
  include ElementContentTextMixin
5
+ include ElementTextMixin
5
6
  include DrawBackgroundMixin
6
7
 
7
- def default_padding_for(side)
8
- return super unless side.to_s == 'top'
9
- @padding_top || 0
10
- end
11
-
12
- def draw_in(rect)
13
- size_to_fit_if_needed or set_text_position
8
+ def draw_options
14
9
  options = computed_options
15
-
16
- return if computed_options[:hidden]
17
-
18
- # render background and border
19
- background_rect = CGRectMake(computed_left, computed_top, computed_outer_width, computed_outer_height)
20
- draw_background_in(background_rect, options)
21
-
22
- # render text
23
- color = (options[:text_color] || :black).uicolor
10
+ text = (options[:html] || options[:text]).to_s.gsub(/^[\n\r]+/, '')
11
+ text_color = (options[:text_color] || :black).uicolor
24
12
  font = (options[:font] || :system).uifont
25
- alignment = (options.has_key?(:text_alignment) ? options[:text_alignment] : :left).uitextalignment
26
- line_break_mode = (options.has_key?(:line_break_mode) ? options[:line_break_mode] : :tail_truncation).uilinebreakmode
27
- label_text = options[:text].to_s.gsub(/^[\n\r]+/, '')
13
+
14
+ text_alignment_name = options.has_key?(:text_alignment) ? options[:text_alignment] : :left
15
+ text_alignment = text_alignment_name.uitextalignment
16
+ line_break_mode_name = options.has_key?(:line_break_mode) ? options[:line_break_mode] : :tail_truncation
17
+ line_break_mode = line_break_mode_name.uilinebreakmode
28
18
 
29
19
  top_left_corner = CGPointMake(computed_inner_left, computed_inner_top)
30
20
  if options[:number_of_lines].to_i.zero?
31
- rect = CGRectMake(*top_left_corner.to_a, computed_width, computed_height)
21
+ inner_rect = CGRectMake(*top_left_corner.to_a, computed_width, computed_height)
32
22
  end
23
+ super.merge({
24
+ text: text,
25
+ is_html: options[:html].present?,
26
+ text_color: text_color,
27
+ font: font,
28
+ text_alignment_name: text_alignment_name,
29
+ text_alignment: text_alignment,
30
+ line_break_mode_name: line_break_mode_name,
31
+ line_break_mode: line_break_mode,
32
+ line_spacing: options[:line_spacing],
33
+ underline: options[:underline],
34
+ top_left_corner: top_left_corner,
35
+ inner_rect: inner_rect
36
+ })
37
+ end
33
38
 
34
- if options[:line_spacing] || options[:underline]
35
- # attributed string
36
- paragrahStyle = NSMutableParagraphStyle.alloc.init
39
+ def draw_in(rect)
40
+ draw_in_context(UIGraphicsGetCurrentContext())
41
+ end
37
42
 
38
- paragrahStyle.setLineSpacing(options[:line_spacing]) if options[:line_spacing]
39
- paragrahStyle.setAlignment(alignment)
40
- paragrahStyle.setLineBreakMode(line_break_mode)
41
- attributes = {}
42
- attributes[NSParagraphStyleAttributeName] = paragrahStyle
43
- attributes[NSForegroundColorAttributeName] = color
44
- attributes[NSFontAttributeName] = font
43
+ def draw_in_context(context)
44
+ return if computed_options[:hidden]
45
+ size_to_fit_if_needed
46
+ set_text_position
45
47
 
46
- label_text = NSMutableAttributedString.alloc.initWithString(label_text, attributes: attributes)
47
- if underline_range = options[:underline]
48
- # FIXME
49
- # label_text = NSMutableAttributedString.alloc.initWithAttributedString(label_text)
50
- # label_text.addAttributes({NSUnderlineStyleAttributeName => NSUnderlineStyleSingle}, range: underline_range)
51
- end
48
+ draw_background_in_context(context)
52
49
 
53
- rect ? label_text.drawInRect(rect) : label_text.drawAtPoint(top_left_corner)
50
+ UIGraphicsPushContext(context)
51
+ options = draw_options
52
+ if options[:is_html] || options[:line_spacing] || options[:underline]
53
+ prepared_text = options[:is_html] ? html_string(options) : attributed_string(options)
54
+
55
+ if options[:inner_rect]
56
+ prepared_text.drawInRect(options[:inner_rect])
57
+ else
58
+ prepared_text.drawAtPoint(options[:top_left_corner])
59
+ end
54
60
  else
55
61
  # regular string
56
- color.set
57
- if rect
58
- label_text.drawInRect(rect,
59
- withFont: font,
60
- lineBreakMode: line_break_mode,
61
- alignment: alignment)
62
+ prepared_text = options[:text]
63
+ options[:text_color].set
64
+ if options[:inner_rect]
65
+ prepared_text.drawInRect(options[:inner_rect],
66
+ withFont: options[:font],
67
+ lineBreakMode: options[:line_break_mode],
68
+ alignment: options[:text_alignment])
62
69
  else
63
- label_text.drawAtPoint(top_left_corner, withFont: font)
70
+ prepared_text.drawAtPoint(options[:top_left_corner], withFont: options[:font])
64
71
  end
65
72
  end
73
+ UIGraphicsPopContext()
74
+ end
75
+
76
+ def default_padding_for(side)
77
+ return super unless side.to_s == 'top'
78
+ @padding_top || 0
66
79
  end
67
80
 
68
81
  def size_to_fit_if_needed
69
82
  if computed_options[:size_to_fit]
70
83
  computed_options[:width] ||= cached_content_outer_width
71
- if computed_options[:width]
72
- computed_options[:height] ||= cached_content_outer_height
73
- end
84
+ computed_options[:height] ||= cached_content_outer_height
74
85
  reset_computed_values
75
- true
76
86
  end
77
87
  end
78
88
 
79
89
  def set_text_position
80
- if computed_options.slice(:padding_top, :padding_bottom, :padding).none?
90
+ if computed_options.slice(:padding_top, :padding_bottom, :padding).values.none?
81
91
  computed_options[:width] ||= computed_width
82
92
  @padding_top = (computed_outer_height - cached_content_height)/2
83
93
  # @padding_top += 1 unless @padding_top.zero?
@@ -4,11 +4,13 @@ module MotionPrime
4
4
  include DrawBackgroundMixin
5
5
 
6
6
  def draw_in(rect)
7
+ draw_in_context(UIGraphicsGetCurrentContext())
8
+ end
9
+
10
+ def draw_in_context(context)
7
11
  return if computed_options[:hidden]
8
- options = computed_options
9
12
 
10
- background_rect = CGRectMake(computed_left, computed_top, computed_outer_width, computed_outer_height)
11
- draw_background_in(background_rect, options)
13
+ draw_background_in_context(context)
12
14
  end
13
15
  end
14
16
  end
@@ -5,9 +5,7 @@ module MotionPrime
5
5
  [*base_styles].each do |base_style|
6
6
  [*suffixes].each do |suffix|
7
7
  components = []
8
- components << base_style.to_s if base_style.present?
9
- components << suffix.to_s if suffix.present?
10
- styles << components.join('_').to_sym if components.present?
8
+ styles << [base_style.to_s, suffix.to_s].join('_').to_sym if base_style.present? && suffix.present?
11
9
  end
12
10
  end
13
11
  styles
@@ -46,6 +46,14 @@ module MotionPrime
46
46
  data
47
47
  end
48
48
 
49
+ def last
50
+ all.last
51
+ end
52
+
53
+ def first
54
+ all.first
55
+ end
56
+
49
57
  def set_inverse_relation_for(models)
50
58
  [*models].each do |model|
51
59
  model.send("#{inverse_relation_name}=", inverse_relation)
@@ -22,6 +22,14 @@ module MotionPrime
22
22
  end
23
23
  end
24
24
 
25
+ def last
26
+ all.last
27
+ end
28
+
29
+ def first
30
+ all.first
31
+ end
32
+
25
33
  # Find model by criteria
26
34
  #
27
35
  # Examples:
data/motion-prime/mp.rb CHANGED
@@ -10,6 +10,10 @@ module MotionPrime
10
10
  def self.low_camelize_factory_cache
11
11
  @camelize_factory_cache ||= {}
12
12
  end
13
+
14
+ def self.env
15
+ ENV['PRIME_ENV'] || ENV['RUBYMOTION_ENV'] || 'development'
16
+ end
13
17
  end
14
18
  ::MP = MotionPrime unless defined?(::MP)
15
19
  ::Prime = MotionPrime unless defined?(::Prime)
@@ -99,8 +99,12 @@ module MotionPrime
99
99
  def close_screen_navigational(args = {})
100
100
  if args[:to_screen] && args[:to_screen].is_a?(UIViewController)
101
101
  self.parent_screen = args[:to_screen]
102
+
103
+ screens = self.navigation_controller.childViewControllers
104
+ app_delegate.close_screens(screens[screens.index(parent_screen)+1..-1])
102
105
  self.navigation_controller.popToViewController(args[:to_screen], animated: args[:animated])
103
106
  else
107
+ app_delegate.close_screens(self)
104
108
  self.navigation_controller.popViewControllerAnimated(args[:animated])
105
109
  end
106
110
  send_on_return(args)
@@ -33,5 +33,12 @@ module MotionPrime
33
33
  end
34
34
  @on_appear_happened = true
35
35
  end
36
+
37
+ def on_destroy
38
+ BW::Reactor.schedule do
39
+ pp 'destroying screen'
40
+ @main_section = nil
41
+ end
42
+ end
36
43
  end
37
44
  end
@@ -4,7 +4,7 @@ module MotionPrime
4
4
 
5
5
  def self.new(menu, content, options={})
6
6
  screen = self.alloc.initWithContentViewController(nil, menuViewController: nil)
7
- screen.backgroundImage = MotionPrime::Config.sidebar.background_image
7
+ screen.backgroundImage = Prime::Config.sidebar.background_image
8
8
  screen.parallaxEnabled = false
9
9
 
10
10
  full_width = UIScreen.mainScreen.bounds.size.width
@@ -71,7 +71,7 @@ module MotionPrime
71
71
  controller.send(:on_screen_load) if controller.respond_to?(:on_screen_load)
72
72
  controller = controller.main_controller if controller.respond_to?(:main_controller)
73
73
  else
74
- controller.navigation_controller = content_controller
74
+ controller.navigation_controller = content_controller if controller.respond_to?(:navigation_controller)
75
75
  controller.send(:on_screen_load) if controller.respond_to?(:on_screen_load)
76
76
  end
77
77
  controller
@@ -1,9 +1,16 @@
1
1
  module MotionPrime
2
2
  module CellSectionMixin
3
+ extend ::MotionSupport::Concern
4
+
3
5
  attr_writer :table
6
+ attr_reader :pending_display
7
+
8
+ included do
9
+ class_attribute :custom_cell_name
10
+ end
4
11
 
5
12
  def table
6
- @table ||= options[:table]
13
+ @table ||= options[:table].try(:weak_ref)
7
14
  end
8
15
 
9
16
  def section_styles
@@ -17,22 +24,54 @@ module MotionPrime
17
24
  end
18
25
 
19
26
  def cell_name
20
- self.class.cell_name || begin
27
+ self.class.custom_cell_name || begin
21
28
  return name unless table
22
29
  table_name = table.name.gsub('_table', '')
23
30
  name.gsub("#{table_name}_", '')
24
31
  end
25
32
  end
26
33
 
27
- def load_container_element(options = {})
34
+ def container_bounds
35
+ @container_bounds ||= CGRectMake(0, 0, table.table_view.bounds.size.width, container_height)
36
+ end
37
+
38
+ def init_container_element(options = {})
28
39
  @container_element ||= begin
29
40
  options.merge!({
30
- screen: screen,
31
- section: self,
41
+ screen: screen.try(:weak_ref),
42
+ section: self.weak_ref,
32
43
  has_drawn_content: true
33
44
  })
45
+ options[:styles] ||= []
46
+ options[:styles] = [:"#{table.name}_first_cell"] if table.data.first == self
47
+ options[:styles] = [:"#{table.name}_last_cell"] if table.data.last == self
34
48
  MotionPrime::BaseElement.factory(:table_view_cell, options)
35
49
  end
36
50
  end
51
+
52
+ def load_container_element(options = {})
53
+ init_container_element(options)
54
+ load_elements
55
+ @container_element.compute_options! unless @container_element.computed_options
56
+ if respond_to?(:prerender_elements_for_state) && prerender_enabled?
57
+ prerender_elements_for_state(:normal)
58
+ end
59
+ end
60
+
61
+ def pending_display!
62
+ @pending_display = true
63
+ display unless table.decelerating
64
+ end
65
+
66
+ def display
67
+ @pending_display = false
68
+ container_view.setNeedsDisplay
69
+ end
70
+
71
+ module ClassMethods
72
+ def set_cell_name(value)
73
+ self.custom_cell_name = value
74
+ end
75
+ end
37
76
  end
38
77
  end
@@ -0,0 +1,120 @@
1
+ module MotionPrime
2
+ module DrawSectionMixin
3
+ extend ::MotionSupport::Concern
4
+
5
+ include HasStyles
6
+ include FrameCalculatorMixin
7
+ attr_accessor :container_element, :container_gesture_recognizers, :cached_draw_image
8
+ included do
9
+ class_attribute :prerender_enabled
10
+ end
11
+
12
+ def container_view
13
+ container_element.try(:view)
14
+ end
15
+
16
+ def draw_in(rect, state = :normal)
17
+ if cached_draw_image[state]
18
+ context = UIGraphicsGetCurrentContext()
19
+ CGContextDrawImage(context, container_bounds, cached_draw_image[state])
20
+ render_image_elements
21
+ elsif prerender_enabled?
22
+ prerender_elements_for_state(state)
23
+ draw_in(rect, state)
24
+ else
25
+ draw_background_in_context(UIGraphicsGetCurrentContext(), rect)
26
+ draw_elements(rect)
27
+ end
28
+ end
29
+
30
+ def bind_gesture_on_container_for(element, action, receiver = nil)
31
+ self.container_gesture_recognizers ||= begin
32
+ set_container_gesture_recognizer
33
+ []
34
+ end
35
+ self.container_gesture_recognizers << {element: element, action: action, receiver: receiver}
36
+ end
37
+
38
+ def prerender_elements_for_state(state = :normal)
39
+ scale = UIScreen.mainScreen.scale
40
+ space = CGColorSpaceCreateDeviceRGB()
41
+ bits_per_component = 8
42
+ context = CGBitmapContextCreate(nil, container_bounds.size.width*scale, container_bounds.size.height*scale,
43
+ bits_per_component, container_bounds.size.width*scale*4, space, KCGImageAlphaPremultipliedLast)
44
+
45
+ CGContextScaleCTM(context, scale, scale)
46
+
47
+ draw_background_in_context(context, container_bounds)
48
+ elements_to_draw.each do |key, element|
49
+ element.draw_in_context(context)
50
+ end
51
+
52
+ cached_draw_image[state] = CGBitmapContextCreateImage(context)
53
+ end
54
+
55
+ def prerender_enabled?
56
+ self.class.prerender_enabled
57
+ end
58
+
59
+ def cached_draw_image
60
+ @cached_draw_image ||= MotionSupport::HashWithIndifferentAccess.new
61
+ end
62
+
63
+ private
64
+ def set_container_gesture_recognizer
65
+ single_tap = UITapGestureRecognizer.alloc.initWithTarget(self, action: 'on_container_tap_gesture:')
66
+ single_tap.cancelsTouchesInView = false
67
+ container_view.addGestureRecognizer single_tap
68
+ container_view.setUserInteractionEnabled true
69
+ end
70
+
71
+ def on_container_tap_gesture(recognizer)
72
+ target = Array.wrap(container_gesture_recognizers).detect do |gesture_data|
73
+ CGRectContainsPoint(gesture_data[:element].computed_frame, recognizer.locationInView(container_view))
74
+ end
75
+ (target[:receiver] || self).send(target[:action], recognizer, target[:element]) if target
76
+ end
77
+
78
+ def draw_elements(rect)
79
+ elements_to_draw.each do |key, element|
80
+ element.draw_in(rect)
81
+ end
82
+ end
83
+
84
+ def draw_background_in_context(context, rect)
85
+ return unless container_element
86
+
87
+ options = container_element.computed_options
88
+ background_color = options[:background_color].try(:uicolor)
89
+
90
+ if gradient_options = options[:gradient]
91
+ start_point = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect))
92
+ end_point = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect))
93
+
94
+ # CGContextSaveGState(context)
95
+ CGContextAddRect(context, rect)
96
+ CGContextClip(context)
97
+ gradient = prepare_gradient(gradient_options)
98
+ CGContextDrawLinearGradient(context, gradient, start_point, end_point, 0)
99
+ # CGContextRestoreGState(context)
100
+ elsif background_color && background_color != :clear.uicolor
101
+ UIGraphicsPushContext(context)
102
+ background_color.uicolor.setFill
103
+ UIRectFill(rect)
104
+ UIGraphicsPopContext()
105
+ end
106
+ end
107
+
108
+ def render_image_elements
109
+ elements_to_draw.each do |key, element|
110
+ element.load_image if element.is_a?(ImageDrawElement)
111
+ end
112
+ end
113
+
114
+ module ClassMethods
115
+ def enable_prerender
116
+ self.prerender_enabled = true
117
+ end
118
+ end
119
+ end
120
+ end