motion-prime 0.4.3 → 0.4.4

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