under-os 0.0.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +16 -0
  5. data/README.md +70 -0
  6. data/Rakefile +24 -0
  7. data/app/config.rb +4 -0
  8. data/app/layouts/alerts.html +8 -0
  9. data/app/layouts/calculator.html +26 -0
  10. data/app/layouts/collections.html +7 -0
  11. data/app/layouts/home.html +14 -0
  12. data/app/layouts/http.html +8 -0
  13. data/app/layouts/inputs.html +44 -0
  14. data/app/layouts/lockers.html +6 -0
  15. data/app/layouts/navbar.html +9 -0
  16. data/app/layouts/scrolls.html +42 -0
  17. data/app/layouts/sidebars.html +17 -0
  18. data/app/pages/alerts_page.rb +14 -0
  19. data/app/pages/calculator_page.rb +36 -0
  20. data/app/pages/collections_page.rb +9 -0
  21. data/app/pages/home_page.rb +11 -0
  22. data/app/pages/http_page.rb +33 -0
  23. data/app/pages/inputs_page.rb +9 -0
  24. data/app/pages/lockers_page.rb +16 -0
  25. data/app/pages/navbar_page.rb +19 -0
  26. data/app/pages/scrolls_page.rb +5 -0
  27. data/app/pages/sidebars_page.rb +42 -0
  28. data/app/pages/stuff_page.rb +35 -0
  29. data/app/styles/application.css +25 -0
  30. data/app/styles/calculator.css +37 -0
  31. data/app/styles/collections.css +19 -0
  32. data/app/styles/home.css +0 -0
  33. data/app/styles/http.css +24 -0
  34. data/app/styles/inputs.css +47 -0
  35. data/app/styles/scrolls.css +67 -0
  36. data/app/styles/sidebars.css +19 -0
  37. data/lib/assets/fontawesome-webfont.ttf +0 -0
  38. data/lib/assets/under-os.css +115 -0
  39. data/lib/under-os.rb +9 -1
  40. data/lib/under_os/app.rb +33 -0
  41. data/lib/under_os/color.rb +176 -0
  42. data/lib/under_os/config.rb +28 -1
  43. data/lib/under_os/core/kernel.rb +16 -0
  44. data/lib/under_os/core/numeric.rb +23 -0
  45. data/lib/under_os/core/string.rb +40 -0
  46. data/lib/under_os/delegate.rb +3 -2
  47. data/lib/under_os/events.rb +85 -0
  48. data/lib/under_os/file.rb +110 -0
  49. data/lib/under_os/history.rb +50 -0
  50. data/lib/under_os/http/receiver.rb +44 -0
  51. data/lib/under_os/http/request.rb +89 -0
  52. data/lib/under_os/http/response.rb +24 -0
  53. data/lib/under_os/http/session.rb +49 -0
  54. data/lib/under_os/http.rb +5 -0
  55. data/lib/under_os/page/builder.rb +96 -0
  56. data/lib/under_os/page/layout.rb +43 -0
  57. data/lib/under_os/page/matcher.rb +83 -0
  58. data/lib/under_os/page/stylesheet.rb +67 -0
  59. data/lib/under_os/page.rb +153 -0
  60. data/lib/under_os/parser/css.rb +37 -0
  61. data/lib/under_os/parser/html.rb +97 -0
  62. data/lib/under_os/parser.rb +24 -0
  63. data/lib/under_os/point.rb +47 -0
  64. data/lib/under_os/screen.rb +9 -0
  65. data/lib/under_os/timer.rb +65 -0
  66. data/lib/under_os/ui/alert.rb +52 -0
  67. data/lib/under_os/ui/button.rb +22 -0
  68. data/lib/under_os/ui/collection/cell.rb +21 -0
  69. data/lib/under_os/ui/collection/delegate.rb +68 -0
  70. data/lib/under_os/ui/collection/item.rb +32 -0
  71. data/lib/under_os/ui/collection/layout.rb +43 -0
  72. data/lib/under_os/ui/collection/styles.rb +15 -0
  73. data/lib/under_os/ui/collection.rb +63 -0
  74. data/lib/under_os/ui/icon/awesome.rb +376 -0
  75. data/lib/under_os/ui/icon/engine.rb +10 -0
  76. data/lib/under_os/ui/icon.rb +42 -0
  77. data/lib/under_os/ui/image.rb +30 -0
  78. data/lib/under_os/ui/input.rb +41 -0
  79. data/lib/under_os/ui/label.rb +21 -0
  80. data/lib/under_os/ui/locker.rb +42 -0
  81. data/lib/under_os/ui/navbar.rb +115 -0
  82. data/lib/under_os/ui/progress.rb +17 -0
  83. data/lib/under_os/ui/scroll.rb +41 -0
  84. data/lib/under_os/ui/select.rb +98 -0
  85. data/lib/under_os/ui/sidebar.rb +45 -0
  86. data/lib/under_os/ui/slider.rb +49 -0
  87. data/lib/under_os/ui/spinner.rb +23 -0
  88. data/lib/under_os/ui/style/fonts.rb +60 -0
  89. data/lib/under_os/ui/style/margins.rb +160 -0
  90. data/lib/under_os/ui/style/outlining.rb +98 -0
  91. data/lib/under_os/ui/style/positioning.rb +169 -0
  92. data/lib/under_os/ui/style.rb +21 -0
  93. data/lib/under_os/ui/switch.rb +29 -0
  94. data/lib/under_os/ui/textarea.rb +14 -0
  95. data/lib/under_os/ui/utils/animation.rb +99 -0
  96. data/lib/under_os/ui/utils/commons.rb +70 -0
  97. data/lib/under_os/ui/utils/dimensions.rb +37 -0
  98. data/lib/under_os/ui/utils/editable.rb +43 -0
  99. data/lib/under_os/ui/utils/events.rb +75 -0
  100. data/lib/under_os/ui/utils/manipulation.rb +44 -0
  101. data/lib/under_os/ui/utils/position.rb +21 -0
  102. data/lib/under_os/ui/utils/size.rb +21 -0
  103. data/lib/under_os/ui/utils/styles.rb +89 -0
  104. data/lib/under_os/ui/utils/traversing.rb +44 -0
  105. data/lib/under_os/ui/utils/wrap.rb +77 -0
  106. data/lib/under_os/ui/view.rb +31 -0
  107. data/lib/under_os/ui.rb +3 -0
  108. data/lib/under_os.rb +2 -2
  109. data/resources/Default-568h@2x.png +0 -0
  110. data/resources/test.png +0 -0
  111. data/spec/assets/app.css +13 -0
  112. data/spec/assets/test.css +7 -0
  113. data/spec/assets/test.html +3 -0
  114. data/spec/assets/test_page.rb +2 -0
  115. data/spec/lib/core/numeric_spec.rb +37 -0
  116. data/spec/lib/core/string_spec.rb +87 -0
  117. data/spec/lib/under_os/color_spec.rb +133 -0
  118. data/spec/lib/under_os/events_spec.rb +71 -0
  119. data/spec/lib/under_os/file_spec.rb +98 -0
  120. data/spec/lib/under_os/page/builder_spec.rb +128 -0
  121. data/spec/lib/under_os/page/layout_spec.rb +18 -0
  122. data/spec/lib/under_os/page/matcher_spec.rb +154 -0
  123. data/spec/lib/under_os/page/stylesheet_spec.rb +83 -0
  124. data/spec/lib/under_os/page_spec.rb +5 -0
  125. data/spec/lib/under_os/parser/css_spec.rb +77 -0
  126. data/spec/lib/under_os/parser/html_spec.rb +152 -0
  127. data/spec/lib/under_os/parser_spec.rb +16 -0
  128. data/spec/lib/under_os/point_spec.rb +54 -0
  129. data/spec/lib/under_os/screen_spec.rb +11 -0
  130. data/spec/lib/under_os/timer_spec.rb +93 -0
  131. data/spec/lib/under_os/ui/button_spec.rb +5 -0
  132. data/spec/lib/under_os/ui/collection_spec.rb +19 -0
  133. data/spec/lib/under_os/ui/icon_spec.rb +26 -0
  134. data/spec/lib/under_os/ui/image_spec.rb +39 -0
  135. data/spec/lib/under_os/ui/input_spec.rb +77 -0
  136. data/spec/lib/under_os/ui/label_spec.rb +22 -0
  137. data/spec/lib/under_os/ui/locker_spec.rb +31 -0
  138. data/spec/lib/under_os/ui/progress_spec.rb +31 -0
  139. data/spec/lib/under_os/ui/scroll_spec.rb +40 -0
  140. data/spec/lib/under_os/ui/select_spec.rb +131 -0
  141. data/spec/lib/under_os/ui/sidebar_spec.rb +35 -0
  142. data/spec/lib/under_os/ui/slider_spec.rb +65 -0
  143. data/spec/lib/under_os/ui/spinner_spec.rb +57 -0
  144. data/spec/lib/under_os/ui/style/fonts_spec.rb +73 -0
  145. data/spec/lib/under_os/ui/style/margins_spec.rb +106 -0
  146. data/spec/lib/under_os/ui/style/outlining_spec.rb +69 -0
  147. data/spec/lib/under_os/ui/style/positioning_spec.rb +69 -0
  148. data/spec/lib/under_os/ui/style_spec.rb +19 -0
  149. data/spec/lib/under_os/ui/switch_spec.rb +56 -0
  150. data/spec/lib/under_os/ui/textarea_spec.rb +30 -0
  151. data/spec/lib/under_os/ui/utils/commons_spec.rb +81 -0
  152. data/spec/lib/under_os/ui/utils/manipulation_spec.rb +130 -0
  153. data/spec/lib/under_os/ui/utils/styles_spec.rb +140 -0
  154. data/spec/lib/under_os/ui/utils/traversing_spec.rb +124 -0
  155. data/spec/lib/under_os/ui/utils/wrap_spec.rb +69 -0
  156. data/spec/lib/under_os/ui/view_spec.rb +39 -0
  157. data/under-os.gemspec +32 -0
  158. metadata +225 -11
  159. data/lib/under_os/application.rb +0 -16
  160. data/lib/under_os/window.rb +0 -5
@@ -0,0 +1,153 @@
1
+ #
2
+ # UOS::Page is kind of a wrap over an iOS controller
3
+ # but instead of the controller, it presents more the
4
+ # main (root) view of the controller in a sence you've
5
+ # got for DOM documents in web
6
+ #
7
+ class UnderOs::Page
8
+ include UnderOs::Events
9
+ include UnderOs::UI
10
+
11
+ attr_reader :_, :stylesheet
12
+
13
+ def self.new(*args)
14
+ alloc.setup_wrap(*args)
15
+ end
16
+
17
+ def self.layout(name=nil)
18
+ name ? (@layout = name) : @layout
19
+ end
20
+
21
+ def initialize
22
+ # page building goes in here
23
+ end
24
+
25
+ def view
26
+ @_view
27
+ end
28
+
29
+ def view=(view)
30
+ @_view = view
31
+ end
32
+
33
+ %w[insert append prepend find first].each do |method|
34
+ define_method method do |*args|
35
+ @_view.__send__ method, *args
36
+ end
37
+ end
38
+
39
+ def alert(*args)
40
+ Alert.new(*args)
41
+ end
42
+
43
+ def history
44
+ UnderOs::App.history
45
+ end
46
+
47
+ def navbar
48
+ history.navbar
49
+ end
50
+
51
+ def title
52
+ @_.navigationItem.title
53
+ end
54
+
55
+ def title=(text)
56
+ @_.navigationItem.title = text
57
+ end
58
+
59
+ def name
60
+ self.class.name.underscore.sub(/_page$/, '')
61
+ end
62
+
63
+ def setup_wrap(*args)
64
+ @_ = UIViewControllerWrap.alloc.init(self, {
65
+ on_load_view: Proc.new{ emit('init') },
66
+ on_view_loaded: Proc.new{ emit('load') },
67
+ on_view_appear: Proc.new{ emit('appear') },
68
+ on_view_disappear: Proc.new{ emit('disappear') },
69
+ on_view_rerender: Proc.new{ emit('rerender') },
70
+ on_view_rotate: Proc.new{ emit('rotate') }
71
+ })
72
+
73
+ on 'init' do
74
+ build_layout
75
+ compile_styles
76
+ end
77
+
78
+ on 'load' do
79
+ repaint
80
+ initialize(*args)
81
+ repaint
82
+ end
83
+
84
+ on 'rotate' do
85
+ repaint
86
+ end
87
+
88
+ self
89
+ end
90
+
91
+ def build_layout
92
+ @_layout = Layout.new(self)
93
+ end
94
+
95
+ def compile_styles
96
+ @stylesheet = Stylesheet.new
97
+ @stylesheet << UnderOs::App.stylesheet
98
+ @stylesheet.load("#{name}.css")
99
+ end
100
+
101
+ def repaint
102
+ view.repaint(stylesheet) if view
103
+ navbar.repaint(stylesheet) if navbar
104
+ end
105
+
106
+ #
107
+ # A simple wrap over UIViewController to intercept
108
+ # the iOS events
109
+ #
110
+ class UIViewControllerWrap < UIViewController
111
+ attr_reader :wrapper
112
+
113
+ def init(wrapper, options)
114
+ @wrapper = wrapper
115
+ @options = options
116
+ initWithNibName(nil, bundle: nil)
117
+ end
118
+
119
+ def loadView
120
+ super
121
+ @options[:on_load_view].call
122
+ end
123
+
124
+ def viewDidLoad
125
+ super
126
+ @options[:on_view_loaded].call
127
+ end
128
+
129
+ def viewDidAppear(animated)
130
+ super
131
+ @options[:on_view_appear].call
132
+ end
133
+
134
+ def viewDidDisappear(animated)
135
+ super
136
+ @options[:on_view_disappear].call
137
+ end
138
+
139
+ def viewWillLayoutSubviews
140
+ super
141
+ @options[:on_view_rerender].call
142
+ end
143
+
144
+ def didRotateFromInterfaceOrientation(orientation)
145
+ super
146
+ @options[:on_view_rotate].call
147
+ end
148
+
149
+ def prefersStatusBarHidden
150
+ !UnderOs::App.config.status_bar
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,37 @@
1
+ class UnderOs::Parser::CSS
2
+ def parse(style)
3
+ style = style.gsub(/\/\*[\s\S]+?\*\//, '').strip
4
+
5
+ {}.tap do |result|
6
+ style.scan(/(\A|\})([a-z0-9_\-\.\s#:,]+)\{([^}]+)/).map do |rule|
7
+ values = parse_styles(rule[2])
8
+
9
+ rule[1].split(',').each do |css_rule|
10
+ result[css_rule.gsub(/\s+/, ' ').strip] = values
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ def parse_styles(styles)
17
+ {}.tap do |hash|
18
+ styles.scan(/([a-z\-]+)\s*:\s*([^;]+)\s*/).each do |param|
19
+ hash.merge! normalized_values(param[0], param[1])
20
+ end
21
+ end
22
+ end
23
+
24
+ def normalized_values(key, value)
25
+ key = key.camelize.to_sym
26
+ value = value.strip.gsub(/px$/, '')
27
+ value = value.gsub(/^('|")(.*?)\1$/, '\2')
28
+ value = value.to_f if value =~ /^[\-\d\.]+$/
29
+
30
+ if key == :background && value =~ /^[\S]+$/
31
+ key = :backgroundColor
32
+ end
33
+
34
+
35
+ {key => value}
36
+ end
37
+ end
@@ -0,0 +1,97 @@
1
+ class UnderOs::Parser::HTML
2
+ def parse(html)
3
+ html = html.strip.gsub(/<\!--[\s\S]*?-->/, '').gsub(/>\s+/, '>').gsub(/\s+</, '<')
4
+
5
+ [].tap do |top|
6
+ @top = top
7
+ @stack = []
8
+ @node = nil
9
+ i = 0
10
+
11
+ while i < html.size
12
+ @chunk = html.slice(i, html.size)
13
+
14
+ i += open_tag || close_tag || plain_text
15
+ end
16
+
17
+ # closing all the missing tags
18
+ while node = @stack.shift
19
+ node.delete(:children)
20
+ node.delete(:text)
21
+ @top << node if ! @top.include?(node)
22
+ end
23
+ end
24
+ end
25
+
26
+ def open_tag
27
+ if m = @chunk.match(/\A<([a-z]+)([^>]*)>/)
28
+ @node = {tag: m[1], attrs: parse_attrs_in(m[2])}
29
+
30
+ if parent = @stack.last
31
+ parent[:children] ||= []
32
+ parent[:children] << @node
33
+ parent.delete(:text) # it can have either text or children
34
+ else
35
+ @top << @node
36
+ end
37
+
38
+ @stack << @node
39
+
40
+ m[0].size
41
+ end
42
+ end
43
+
44
+ def close_tag
45
+ if m = @chunk.match(/\A<\/([a-z]+)>/)
46
+ while node = @stack.pop
47
+ if node[:tag] != m[1]
48
+ if @stack.size > 0
49
+ @stack.last[:children] += node[:children] || []
50
+ node.delete(:children)
51
+ node.delete(:text)
52
+ end
53
+ else
54
+ break
55
+ end
56
+ end
57
+
58
+ @node = @stack.last
59
+
60
+ m[0].size
61
+ end
62
+ end
63
+
64
+ def plain_text
65
+ if m = @chunk.match(/\A([^<]+)/)
66
+ @stack.last[:text] = m[1] if @stack.last
67
+
68
+ m[0].size
69
+ end
70
+ end
71
+
72
+ def parse_attrs_in(string)
73
+ merge_data_attrs({}.tap do |hash|
74
+ string.scan(/([a-z][a-z_\-\d]+)=('|")(.+?)(\2)/).each do |match|
75
+ value = match[0] == match[2] ? true : match[2]
76
+ value = true if value == 'true'
77
+ value = false if value == 'false'
78
+ hash[match[0].to_sym] = value
79
+ end
80
+ end)
81
+ end
82
+
83
+ def merge_data_attrs(hash)
84
+ hash.keys.each do |key|
85
+ if key.to_s.starts_with?('data-')
86
+ hash[:data] ||= {}
87
+
88
+ value = hash.delete(key)
89
+ key = key.to_s.gsub(/^data\-/, '').camelize
90
+
91
+ hash[:data][key.to_sym] = value
92
+ end
93
+ end
94
+
95
+ hash
96
+ end
97
+ end
@@ -0,0 +1,24 @@
1
+ #
2
+ # Generic templates/stylesheets parsing engine
3
+ #
4
+ class UnderOs::Parser
5
+ def self.parse(*args)
6
+ @inst ||= new
7
+ @inst.parse *args
8
+ end
9
+
10
+ def initialize
11
+ @css = CSS.new
12
+ @html = HTML.new
13
+ end
14
+
15
+ def parse(filename)
16
+ filepath = NSBundle.mainBundle.pathForResource(filename, ofType:nil)
17
+ content = filepath ? UnderOs::File.read(filepath) : ''
18
+
19
+ case filename.split('.').pop
20
+ when 'css' then @css.parse(content)
21
+ when 'html' then @html.parse(content)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ #
2
+ # Generic Point/Size unit
3
+ #
4
+ class UnderOs::Point
5
+
6
+ def initialize(x, y=nil)
7
+ if x.is_a?(UnderOs::Point)
8
+ y = x.y if x.y
9
+ x = x.x
10
+ elsif x.is_a?(Hash)
11
+ y = x[:y] || x['y'] || nil
12
+ x = x[:x] || x['x'] || nil
13
+ end
14
+
15
+ @x = x
16
+ @y = y
17
+ end
18
+
19
+ def x
20
+ @x
21
+ end
22
+
23
+ def y
24
+ @y
25
+ end
26
+
27
+ def ==(*args)
28
+ point = UnderOs::Point.new(*args) # normalizing
29
+ x == point.x && y == point.y
30
+ end
31
+
32
+ def *(multiplier)
33
+ self.class.new(x: @x * multiplier, y: @y * multiplier)
34
+ end
35
+
36
+ def /(divider)
37
+ self.class.new(x: @x / divider.to_f, y: @y / divider.to_f)
38
+ end
39
+
40
+ def to_s
41
+ "x=#{x} y=#{y}"
42
+ end
43
+
44
+ def inspect
45
+ "#<#{self.class.name}:0x#{__id__.to_s(16)} #{to_s}>"
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ class UnderOs::Screen
2
+ def self.size
3
+ # TODO make it handle the screen orientation
4
+ UnderOs::Point.new({
5
+ x: UIScreen.mainScreen.bounds.size.width,
6
+ y: UIScreen.mainScreen.bounds.size.height
7
+ })
8
+ end
9
+ end
@@ -0,0 +1,65 @@
1
+ class UnderOs::Timer
2
+ def self.in(duration, options={}, &block)
3
+ duration = Duration.new(duration) if duration.is_a?(Numeric)
4
+ new duration.to_f, options.merge(repeat: false), &block
5
+ end
6
+
7
+ def self.every(duration, options={}, &block)
8
+ duration = Duration.new(duration) if duration.is_a?(Numeric)
9
+ new duration.to_f, options.merge(repeat: true), &block
10
+ end
11
+
12
+ attr_reader :block, :counter, :interval, :repeats, :_
13
+
14
+ def initialize(seconds, options={}, &block)
15
+ @block = block
16
+ @counter = options[:repeat].to_i if options[:repeat].is_a?(Numeric)
17
+ @interval = seconds
18
+ @repeats = options[:repeat] != false
19
+
20
+ @_ = NSTimer.scheduledTimerWithTimeInterval @interval,
21
+ target: self, selector: :kick, userInfo: nil, repeats: @repeats
22
+ end
23
+
24
+ def stop
25
+ @_.invalidate
26
+ self
27
+ end
28
+
29
+ def kick
30
+ @block.call
31
+ stop if @counter && (@counter -= 1) <= 0
32
+ end
33
+
34
+ class Duration
35
+ attr_reader :seconds
36
+
37
+ def initialize(seconds)
38
+ @seconds = seconds.to_f
39
+ end
40
+
41
+ def to_f
42
+ @seconds
43
+ end
44
+
45
+ def to_i
46
+ to_f.to_i
47
+ end
48
+
49
+ def to_s
50
+ "#{to_f} seconds"
51
+ end
52
+
53
+ def ==(duration)
54
+ duration.is_a?(UnderOs::Timer::Duration) && duration.seconds == @seconds
55
+ end
56
+
57
+ def later(options={}, &block)
58
+ UnderOs::Timer.in self, options={}, &block
59
+ end
60
+
61
+ def repeat(options={}, &block)
62
+ UnderOs::Timer.every self, options={}, &block
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,52 @@
1
+ class UnderOs::UI::Alert
2
+ include UnderOs::Events
3
+
4
+ def initialize(options={})
5
+ options = {message: options} if options.is_a?(String)
6
+ self.on = options.delete[:on] if options[:on]
7
+
8
+ @_ = CustomAlertView.alloc.initiWithOptions(self, options)
9
+ @_.show unless options[:show] == false
10
+ end
11
+
12
+ def show
13
+ @_.show
14
+ self
15
+ end
16
+
17
+ def hide(animated=true)
18
+ @_.dismissWithClickedButtonIndex(0, animated: animated)
19
+ self
20
+ end
21
+
22
+ def visible
23
+ @_.visible
24
+ end
25
+
26
+ def hidden
27
+ !visible
28
+ end
29
+
30
+ class CustomAlertView < UIAlertView
31
+ def initiWithOptions(wrapper, options)
32
+ @wrapper = wrapper
33
+
34
+ initWithTitle( options[:title] || "",
35
+ message: options[:message] || "No message given",
36
+ delegate: self,
37
+ cancelButtonTitle: options[:button] || "Ok",
38
+ otherButtonTitles: nil )
39
+
40
+ (options[:buttons] || []).each do |title|
41
+ addButtonWithTitle title
42
+ end
43
+
44
+ self
45
+ end
46
+
47
+ def alertView(alertView, willDismissWithButtonIndex:buttonIndex)
48
+ @wrapper.emit(:tap, buttonIndex: buttonIndex, buttonTitle: buttonTitleAtIndex(buttonIndex))
49
+ @wrapper.emit(:close)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,22 @@
1
+ class UnderOS::UI::Button < UnderOS::UI::View
2
+ wraps UIButton, tag: 'button'
3
+
4
+ def initialize(options={})
5
+ super
6
+
7
+ self.text = options.delete(:text) || ''
8
+ @_.showsTouchWhenHighlighted = true
9
+ @_.setBackgroundImage(options.delete(:image), forState:UIControlStateNormal) if options[:image]
10
+ @_.sizeToFit
11
+ end
12
+
13
+ def text
14
+ @_.currentTitle
15
+ end
16
+
17
+ def text=(new_text, state=UIControlStateNormal)
18
+ @_.setTitle new_text, forState:state
19
+ repaint
20
+ end
21
+
22
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # This is a bridge to the iOS native functionality
3
+ # please do not use it in your code
4
+ #
5
+ class UnderOs::UI::Collection::Cell < UICollectionViewCell
6
+
7
+ def self.classes
8
+ @classes ||= {}
9
+ end
10
+
11
+ def uos_view_for(collection)
12
+ @uos_view ||= self.class.classes[collection].for(collection).tap do |view|
13
+ contentView.addSubview(view._)
14
+ end
15
+ end
16
+
17
+ def prepareForReuse
18
+ super
19
+ @uos_view.cleanup if @uos_view
20
+ end
21
+ end
@@ -0,0 +1,68 @@
1
+ #
2
+ # The iOS level events receiver, not for a public use
3
+ #
4
+ class UnderOs::UI::Collection::Delegate < UIViewController
5
+
6
+ def self.new(collection)
7
+ alloc.tap do |instance|
8
+ instance.instance_eval do
9
+ @collection = collection
10
+ end
11
+ end
12
+ end
13
+
14
+ ################################################################
15
+ # DataSource API
16
+ ################################################################
17
+
18
+ def numberOfSectionsInCollectionView(collection)
19
+ @collection.number_of_sections
20
+ end
21
+
22
+ def collectionView(collection, numberOfItemsInSection: section)
23
+ @collection.number_of_items(section)
24
+ end
25
+
26
+ def collectionView(collection, cellForItemAtIndexPath: indexPath)
27
+ collection.dequeueReusableCellWithReuseIdentifier('UOSCollectionCell', forIndexPath:indexPath).tap do |item|
28
+ @collection.emit(:item, item: item.uos_view_for(@collection), index: indexPath.row, section: indexPath.section)
29
+ end
30
+ end
31
+
32
+ # def collectionView(collection, viewForSupplementaryElementOfKind: kind, atIndexPath: indexPath)
33
+ # # kind is a string
34
+ # UICollectionReusableView.alloc.init
35
+ # UICollectionReusableView * reusableview = nil ;
36
+
37
+ # if ( kind == UICollectionElementKindSectionHeader ) {
38
+ # RecipeCollectionHeaderView * headerView = [ collectionView dequeueReusableSupplementaryViewOfKind : UICollectionElementKindSectionHeader withReuseIdentifier : @ "HeaderView" forIndexPath : indexPath ] ;
39
+ # NSString * title = [ [ NSString alloc ] initWithFormat : @ "Recipe Group #%i" , indexPath.section + 1 ] ;
40
+ # headerView.title.text = title;
41
+ # UIImage * headerImage = [ UIImage imageNamed : @ "header_banner.png" ] ;
42
+ # headerView.backgroundImage.image = headerImage;
43
+
44
+ # reusableview = headerView;
45
+ # }
46
+
47
+ # if ( kind == UICollectionElementKindSectionFooter ) {
48
+ # UICollectionReusableView * footerview = [ collectionView dequeueReusableSupplementaryViewOfKind : UICollectionElementKindSectionFooter withReuseIdentifier : @ "FooterView" forIndexPath : indexPath ] ;
49
+
50
+ # reusableview = footerview;
51
+ # }
52
+
53
+ # return reusableview;
54
+ # end
55
+
56
+
57
+ ################################################################
58
+ # DataSource API
59
+ ################################################################
60
+
61
+ def collectionView(collection, didSelectItemAtIndexPath: indexPath)
62
+ @collection.emit(:select, index: indexPath.row, section: indexPath.section)
63
+ end
64
+
65
+ def collectionView(collection, didDeselectItemAtIndexPath: indexPath)
66
+ @collection.emit(:unselect, index: indexPath.row, section: indexPath.section)
67
+ end
68
+ end
@@ -0,0 +1,32 @@
1
+ #
2
+ # This is the collection item class that you supposed
3
+ # to inherit (well, if you need to)
4
+ #
5
+ class UnderOs::UI::Collection::Item < UnderOs::UI::View
6
+
7
+ tag :item
8
+
9
+ def self.build(collection, &builder)
10
+ @builders ||= {}
11
+ @builders[collection] = builder
12
+ collection.item_class = self
13
+ end
14
+
15
+ def self.for(collection, stylesheet=nil)
16
+ new.tap do |view|
17
+ def view.parent; @_parent; end
18
+ view.instance_variable_set('@_parent', collection)
19
+
20
+ @builders[collection] && @builders[collection].call.each do |child|
21
+ view.insert child
22
+ end
23
+
24
+ view.repaint(stylesheet || UnderOs::App.history.current_page.stylesheet)
25
+ end
26
+ end
27
+
28
+ def cleanup
29
+ # implement me if you need to clean cells between reuse
30
+ end
31
+
32
+ end
@@ -0,0 +1,43 @@
1
+ #
2
+ # The basic, grid (flow) layout. inheritable.
3
+ #
4
+ class Layout
5
+ attr_reader :_
6
+
7
+ def initialize(layout=nil)
8
+ @_ = layout || UICollectionViewFlowLayout.alloc.init
9
+
10
+ self.items_spacing = 0
11
+ self.rows_spacing = 1
12
+ end
13
+
14
+ def item_size
15
+ end
16
+
17
+ def item_size=(*size)
18
+ size = UnderOs::Point.new(*size)
19
+ @_.itemSize = CGSizeMake(size.x, size.y)
20
+ end
21
+
22
+ def items_spacing
23
+ @_.minimumInteritemSpacing
24
+ end
25
+
26
+ def items_spacing=(value)
27
+ @_.minimumInteritemSpacing = value
28
+ end
29
+
30
+ def rows_spacing
31
+ @_.minimumLineSpacing
32
+ end
33
+
34
+ def rows_spacing=(value)
35
+ @_.minimumLineSpacing = value
36
+ end
37
+
38
+ def section_inset
39
+ end
40
+
41
+ def section_inset=(value)
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ #
2
+ # This thing catches styles from the collection element
3
+ # and adjusts the layout settings for them
4
+ #
5
+ class UnderOs::UI::Collection::Styles
6
+ def self.build(collection, stylesheet)
7
+ item = UnderOs::UI::Collection::Item.for(collection, stylesheet)
8
+
9
+ collection.layout.item_size = item.size
10
+ collection.layout.items_spacing = item.style.marginLeft + item.style.marginRight
11
+ collection.layout.rows_spacing = item.style.marginTop + item.style.marginBottom
12
+
13
+ # TODO header/footer
14
+ end
15
+ end