under-os-ui 1.4.0
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.
- checksums.yaml +7 -0
- data/README.md +26 -0
- data/lib/assets/fontawesome-webfont.ttf +0 -0
- data/lib/assets/under-os.css +115 -0
- data/lib/core/kernel.rb +16 -0
- data/lib/under-os-ui.rb +6 -0
- data/lib/under_os/app.rb +26 -0
- data/lib/under_os/config.rb +25 -0
- data/lib/under_os/history.rb +53 -0
- data/lib/under_os/page.rb +178 -0
- data/lib/under_os/page/builder.rb +96 -0
- data/lib/under_os/page/layout.rb +43 -0
- data/lib/under_os/page/matcher.rb +128 -0
- data/lib/under_os/page/stylesheet.rb +67 -0
- data/lib/under_os/parser.rb +24 -0
- data/lib/under_os/parser/css.rb +37 -0
- data/lib/under_os/parser/html.rb +97 -0
- data/lib/under_os/ui.rb +3 -0
- data/lib/under_os/ui/alert.rb +52 -0
- data/lib/under_os/ui/button.rb +42 -0
- data/lib/under_os/ui/collection.rb +65 -0
- data/lib/under_os/ui/collection/cell.rb +21 -0
- data/lib/under_os/ui/collection/delegate.rb +70 -0
- data/lib/under_os/ui/collection/item.rb +32 -0
- data/lib/under_os/ui/collection/layout.rb +43 -0
- data/lib/under_os/ui/collection/styles.rb +15 -0
- data/lib/under_os/ui/div.rb +3 -0
- data/lib/under_os/ui/form.rb +60 -0
- data/lib/under_os/ui/icon.rb +61 -0
- data/lib/under_os/ui/icon/awesome.rb +376 -0
- data/lib/under_os/ui/icon/engine.rb +9 -0
- data/lib/under_os/ui/image.rb +31 -0
- data/lib/under_os/ui/input.rb +140 -0
- data/lib/under_os/ui/label.rb +21 -0
- data/lib/under_os/ui/locker.rb +42 -0
- data/lib/under_os/ui/navbar.rb +123 -0
- data/lib/under_os/ui/progress.rb +17 -0
- data/lib/under_os/ui/scroll.rb +102 -0
- data/lib/under_os/ui/select.rb +95 -0
- data/lib/under_os/ui/sidebar.rb +45 -0
- data/lib/under_os/ui/slider.rb +37 -0
- data/lib/under_os/ui/spinner.rb +23 -0
- data/lib/under_os/ui/style.rb +21 -0
- data/lib/under_os/ui/style/fonts.rb +56 -0
- data/lib/under_os/ui/style/margins.rb +164 -0
- data/lib/under_os/ui/style/outlining.rb +170 -0
- data/lib/under_os/ui/style/positioning.rb +183 -0
- data/lib/under_os/ui/switch.rb +26 -0
- data/lib/under_os/ui/textarea.rb +19 -0
- data/lib/under_os/ui/utils/animation.rb +101 -0
- data/lib/under_os/ui/utils/commons.rb +70 -0
- data/lib/under_os/ui/utils/dimensions.rb +37 -0
- data/lib/under_os/ui/utils/events.rb +210 -0
- data/lib/under_os/ui/utils/manipulation.rb +44 -0
- data/lib/under_os/ui/utils/position.rb +21 -0
- data/lib/under_os/ui/utils/size.rb +21 -0
- data/lib/under_os/ui/utils/styles.rb +89 -0
- data/lib/under_os/ui/utils/traversing.rb +44 -0
- data/lib/under_os/ui/utils/wrap.rb +77 -0
- data/lib/under_os/ui/view.rb +31 -0
- data/spec/assets/app.css +13 -0
- data/spec/assets/test.css +7 -0
- data/spec/assets/test.html +3 -0
- data/spec/assets/test.png +0 -0
- data/spec/assets/test_page.rb +2 -0
- data/spec/under_os/page/builder_spec.rb +128 -0
- data/spec/under_os/page/layout_spec.rb +18 -0
- data/spec/under_os/page/matcher_spec.rb +260 -0
- data/spec/under_os/page/stylesheet_spec.rb +83 -0
- data/spec/under_os/page_spec.rb +5 -0
- data/spec/under_os/parser/css_spec.rb +77 -0
- data/spec/under_os/parser/html_spec.rb +152 -0
- data/spec/under_os/parser_spec.rb +16 -0
- data/spec/under_os/ui/button_spec.rb +50 -0
- data/spec/under_os/ui/collection_spec.rb +19 -0
- data/spec/under_os/ui/div_spec.rb +24 -0
- data/spec/under_os/ui/form_spec.rb +156 -0
- data/spec/under_os/ui/icon_spec.rb +57 -0
- data/spec/under_os/ui/image_spec.rb +39 -0
- data/spec/under_os/ui/input_spec.rb +109 -0
- data/spec/under_os/ui/label_spec.rb +22 -0
- data/spec/under_os/ui/locker_spec.rb +31 -0
- data/spec/under_os/ui/progress_spec.rb +31 -0
- data/spec/under_os/ui/scroll_spec.rb +75 -0
- data/spec/under_os/ui/select_spec.rb +135 -0
- data/spec/under_os/ui/sidebar_spec.rb +35 -0
- data/spec/under_os/ui/slider_spec.rb +69 -0
- data/spec/under_os/ui/spinner_spec.rb +57 -0
- data/spec/under_os/ui/style/fonts_spec.rb +111 -0
- data/spec/under_os/ui/style/margins_spec.rb +106 -0
- data/spec/under_os/ui/style/outlining_spec.rb +101 -0
- data/spec/under_os/ui/style/positioning_spec.rb +69 -0
- data/spec/under_os/ui/style_spec.rb +19 -0
- data/spec/under_os/ui/switch_spec.rb +60 -0
- data/spec/under_os/ui/textarea_spec.rb +34 -0
- data/spec/under_os/ui/utils/commons_spec.rb +81 -0
- data/spec/under_os/ui/utils/events_spec.rb +87 -0
- data/spec/under_os/ui/utils/manipulation_spec.rb +130 -0
- data/spec/under_os/ui/utils/styles_spec.rb +140 -0
- data/spec/under_os/ui/utils/traversing_spec.rb +124 -0
- data/spec/under_os/ui/utils/wrap_spec.rb +69 -0
- data/spec/under_os/ui/view_spec.rb +39 -0
- data/under-os-ui.gemspec +23 -0
- metadata +216 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This module contains things like positions and sizes of the elements
|
|
3
|
+
#
|
|
4
|
+
module UnderOs::UI
|
|
5
|
+
class Style
|
|
6
|
+
module Positioning
|
|
7
|
+
def width
|
|
8
|
+
@view.frame.size.width
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def width=(width)
|
|
12
|
+
@view.frame = @view.frame.tap do |frame|
|
|
13
|
+
frame.size.width = convert_size(width, :x)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def height
|
|
18
|
+
@view.frame.size.height
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def height=(height)
|
|
22
|
+
@view.frame = @view.frame.tap do |frame|
|
|
23
|
+
frame.size.height = convert_size(height, :y)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def top
|
|
28
|
+
@view.frame.origin.y
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def top=(top)
|
|
32
|
+
@view.frame = @view.frame.tap do |frame|
|
|
33
|
+
frame.origin.y = convert_size(top, :y)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def left
|
|
38
|
+
@view.frame.origin.x
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def left=(left)
|
|
42
|
+
@view.frame = @view.frame.tap do |frame|
|
|
43
|
+
frame.origin.x = convert_size(left, :x)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def right
|
|
48
|
+
parent_size.x - left
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def right=(right)
|
|
52
|
+
@view.frame = @view.frame.tap do |frame|
|
|
53
|
+
frame.origin.x = parent_size[:x] - convert_size(right, :x) - frame.size.width
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def bottom
|
|
58
|
+
parent_size.y - top
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def bottom=(bottom)
|
|
62
|
+
@view.frame = @view.frame.tap do |frame|
|
|
63
|
+
frame.origin.y = parent_size[:y] - convert_size(bottom, :y) - frame.size.height
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def contentWidth
|
|
68
|
+
@view.contentSize.width rescue 0
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def contentWidth=(value)
|
|
72
|
+
return unless @view.is_a?(UIScrollView)
|
|
73
|
+
|
|
74
|
+
if value == 'auto'
|
|
75
|
+
value = 0
|
|
76
|
+
@view.subviews.each do |view|
|
|
77
|
+
x = view.origin.x + view.size.width
|
|
78
|
+
value = x if x > value
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
@view.contentSize = CGSizeMake(value, contentHeight)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def contentHeight
|
|
86
|
+
@view.contentSize.height rescue 0
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def contentHeight=(value)
|
|
90
|
+
return unless @view.is_a?(UIScrollView)
|
|
91
|
+
|
|
92
|
+
if value == 'auto'
|
|
93
|
+
value = 0
|
|
94
|
+
@view.subviews.each do |view|
|
|
95
|
+
y = view.origin.y + view.size.height
|
|
96
|
+
value = y if y > value
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
@view.contentSize = CGSizeMake(contentWidth, value)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def zIndex
|
|
104
|
+
@view.layer.zPosition
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def zIndex=(number)
|
|
108
|
+
@view.layer.zPosition = number
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def overflow
|
|
112
|
+
case "#{overflowX}-#{overflowY}"
|
|
113
|
+
when 'visible-visible' then :visible
|
|
114
|
+
when 'hidden-hidden' then :hidden
|
|
115
|
+
when 'scroll-scroll' then :scroll
|
|
116
|
+
when 'scroll-visible' then :x
|
|
117
|
+
when 'visible-scroll' then :y
|
|
118
|
+
else [overflowX, overflowY]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def overflow=(value)
|
|
123
|
+
x, y = case value.to_s
|
|
124
|
+
when 'x' then ['scroll', 'visible']
|
|
125
|
+
when 'y' then ['visible', 'scroll']
|
|
126
|
+
when 'hidden' then ['hidden', 'hidden']
|
|
127
|
+
when 'visible' then ['visible', 'visible']
|
|
128
|
+
else ['scroll', 'scroll']
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
self.overflowX = x
|
|
132
|
+
self.overflowY = y
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def overflowX
|
|
136
|
+
@view.isScrollEnabled ? @view.showsHorizontalScrollIndicator ? :scroll : :hidden : :visible
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def overflowX=(value)
|
|
140
|
+
return unless @view.is_a?(UIScrollView)
|
|
141
|
+
@view.directionalLockEnabled = overflowY == :visible
|
|
142
|
+
@view.showsHorizontalScrollIndicator = value.to_s == 'scroll'
|
|
143
|
+
@view.scrollEnabled = value.to_s != 'visible' || overflowY != :visible
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def overflowY
|
|
147
|
+
@view.isScrollEnabled ? @view.showsVerticalScrollIndicator ? :scroll : :hidden : :visible
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def overflowY=(value)
|
|
151
|
+
return unless @view.is_a?(UIScrollView)
|
|
152
|
+
@view.directionalLockEnabled = overflowX == :visible
|
|
153
|
+
@view.showsVerticalScrollIndicator = value.to_s == 'scroll'
|
|
154
|
+
@view.scrollEnabled = overflowX != :visible || value.to_s != 'visible'
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
private
|
|
158
|
+
|
|
159
|
+
def convert_size(size, dim)
|
|
160
|
+
if size.is_a?(String)
|
|
161
|
+
if size.ends_with?('%')
|
|
162
|
+
size = size.slice(0, size.size-1).to_f
|
|
163
|
+
size = parent_size[dim] / 100.0 * size
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
size
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def parent_size
|
|
171
|
+
parent = view.superview
|
|
172
|
+
|
|
173
|
+
if !parent.superview && parent == UnderOs::App.history.current_page.view._
|
|
174
|
+
parent = UIScreen.mainScreen.bounds.size
|
|
175
|
+
else
|
|
176
|
+
parent = parent.frame.size
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
{x: parent.width, y: parent.height}
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class UnderOs::UI::Switch < UnderOs::UI::Input
|
|
2
|
+
wraps UISwitch, tag: 'switch'
|
|
3
|
+
|
|
4
|
+
def initialize(options={})
|
|
5
|
+
super
|
|
6
|
+
self.checked = options[:checked] if options[:checked]
|
|
7
|
+
@_.addTarget self, action: :handle_change, forControlEvents:UIControlEventValueChanged
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def value
|
|
11
|
+
@_value
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def value=(value)
|
|
15
|
+
@_value = value # just saving it on the instance
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def checked
|
|
19
|
+
@_.on?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def checked=(flag)
|
|
23
|
+
@_.setOn flag, animated: true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class UnderOs::UI::Textarea < UnderOs::UI::Input
|
|
2
|
+
wraps UITextView, tag: 'textarea'
|
|
3
|
+
|
|
4
|
+
alias :text= :value= # to get values from the HTML layouts
|
|
5
|
+
|
|
6
|
+
# delegate
|
|
7
|
+
|
|
8
|
+
def textViewDidBeginEditing(textView)
|
|
9
|
+
handle_focus
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def textViewDidChange(textView)
|
|
13
|
+
handle_change
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def textViewDidEndEditing(textView)
|
|
17
|
+
handle_blur
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
module UnderOs::UI::Animation
|
|
2
|
+
|
|
3
|
+
def animate(style, options={}, &block)
|
|
4
|
+
if block_given?
|
|
5
|
+
options = style
|
|
6
|
+
style = nil
|
|
7
|
+
else
|
|
8
|
+
[:complete, :curve, :autoreverse, :repeat, :duration, :delay].each do |key|
|
|
9
|
+
options[key] = style.delete(key) if style.has_key?(key)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
block = Proc.new{ self.style = style }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Animation.new(self, options, &block)
|
|
16
|
+
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def highlight(color=:yellow, options={})
|
|
21
|
+
old_color = style.background
|
|
22
|
+
|
|
23
|
+
animate({background: color}, {curve: :ease_out}.merge(options))
|
|
24
|
+
animate({background: old_color}, {curve: :ease_in}.merge(options))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def fade_in(options={})
|
|
28
|
+
animate({opacity: 1}, options)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def fade_out(options={})
|
|
32
|
+
animate({opacity: 0}, options)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class Animation
|
|
36
|
+
CURVES = {
|
|
37
|
+
ease_in_out: UIViewAnimationOptionCurveEaseInOut,
|
|
38
|
+
ease_in: UIViewAnimationOptionCurveEaseIn,
|
|
39
|
+
ease_out: UIViewAnimationOptionCurveEaseOut,
|
|
40
|
+
linear: UIViewAnimationOptionCurveLinear
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
FX_QUEUE = {}
|
|
44
|
+
|
|
45
|
+
def initialize(view, options, &block)
|
|
46
|
+
@view = view
|
|
47
|
+
@options = options
|
|
48
|
+
@block = block
|
|
49
|
+
|
|
50
|
+
@options = {duration: options} if options.is_a?(Numeric)
|
|
51
|
+
|
|
52
|
+
@options[:schedule] == false ? run : schedule
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def queue
|
|
56
|
+
FX_QUEUE[@view] ||= []
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def schedule
|
|
60
|
+
if queue.empty?
|
|
61
|
+
run
|
|
62
|
+
else
|
|
63
|
+
queue << self
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def run
|
|
68
|
+
@view.emit('animation:start')
|
|
69
|
+
|
|
70
|
+
UIView.animateWithDuration duration,
|
|
71
|
+
delay: delay,
|
|
72
|
+
options: options,
|
|
73
|
+
animations: ->{ @block.call },
|
|
74
|
+
completion: ->(finished){ run_next }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def duration
|
|
78
|
+
@options[:duration] || 0.25
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def delay
|
|
82
|
+
@options[:delay] || 0.0
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def options
|
|
86
|
+
options = CURVES[@options[:curve]] || @options[:curve] || UIViewAnimationCurveEaseOut
|
|
87
|
+
options = options | UIViewAnimationOptionAutoreverse if @options[:autoreverse]
|
|
88
|
+
options = options | UIViewAnimationOptionRepeat if @options[:repeat]
|
|
89
|
+
options | UIViewAnimationOptionAllowUserInteraction
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def run_next
|
|
93
|
+
@view.emit('animation:finished')
|
|
94
|
+
@options[:complete].call if @options[:complete]
|
|
95
|
+
|
|
96
|
+
if next_fx = queue.shift
|
|
97
|
+
next_fx.run
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module UnderOs::UI::Commons
|
|
2
|
+
def id
|
|
3
|
+
@_id
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def id=(id)
|
|
7
|
+
@_id = id
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def tagName
|
|
11
|
+
@_tag_name ||= begin
|
|
12
|
+
wraps = UnderOs::UI::Wrap::WRAPS_TAGS_MAP
|
|
13
|
+
klass = self.class; tag = 'VIEW'
|
|
14
|
+
|
|
15
|
+
while klass
|
|
16
|
+
if wrap = wraps.detect{|t,k| k == klass}
|
|
17
|
+
tag = wrap[0].upcase
|
|
18
|
+
break
|
|
19
|
+
else
|
|
20
|
+
klass = klass.superclass
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
tag
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def data(key=nil)
|
|
29
|
+
key && @_data ? @_data[key.to_sym] : @_data
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def data=(hash)
|
|
33
|
+
@_data = hash
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def page
|
|
37
|
+
resp = @_
|
|
38
|
+
|
|
39
|
+
while resp = resp.nextResponder
|
|
40
|
+
if resp.is_a?(UIViewController)
|
|
41
|
+
return resp.wrapper
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def hide
|
|
49
|
+
style.display = :none
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def show
|
|
53
|
+
style.display = :block
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def toggle
|
|
57
|
+
hidden ? show : hide
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def hidden
|
|
61
|
+
@_.isHidden
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def visible
|
|
65
|
+
!hidden
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
alias :hidden? :hidden
|
|
69
|
+
alias :visible? :visible
|
|
70
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This module handles generic ui dimensions and positions of views
|
|
3
|
+
#
|
|
4
|
+
module UnderOs::UI::Dimensions
|
|
5
|
+
def size(size=nil)
|
|
6
|
+
if size
|
|
7
|
+
self.size = size
|
|
8
|
+
self
|
|
9
|
+
else
|
|
10
|
+
@size ||= UnderOs::UI::Size.new(self)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def size=(size)
|
|
15
|
+
size = UnderOs::Point.new(size) # cleaning up
|
|
16
|
+
|
|
17
|
+
self.size.x = size.x if size.x
|
|
18
|
+
self.size.y = size.y if size.y
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def position(position=nil)
|
|
22
|
+
if position
|
|
23
|
+
self.position = position
|
|
24
|
+
self
|
|
25
|
+
else
|
|
26
|
+
@position ||= UnderOs::UI::Position.new(self)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def position=(position)
|
|
31
|
+
position = UnderOs::Point.new(position) # cleaning up
|
|
32
|
+
|
|
33
|
+
self.position.x = position.x if position.x
|
|
34
|
+
self.position.y = position.y if position.y
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Special, UI avare events API
|
|
3
|
+
#
|
|
4
|
+
module UnderOs::UI::Events
|
|
5
|
+
|
|
6
|
+
def on(event, *args, &block)
|
|
7
|
+
return event.map{|e,b| self.on(e,&b)}[0] || self if event.is_a?(Hash)
|
|
8
|
+
|
|
9
|
+
@_.userInteractionEnabled = true
|
|
10
|
+
|
|
11
|
+
event = add_ui_event_listener(event)
|
|
12
|
+
|
|
13
|
+
UnderOs::Events::Listeners.add(self, event, *args, block)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def off(event)
|
|
17
|
+
UnderOs::Events::Listeners.remove(self, event)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def emit(*event)
|
|
21
|
+
if event.is_a?(UIGestureRecognizer)
|
|
22
|
+
name, r = find_recognizer_from(event.class)
|
|
23
|
+
event = Event.new(event, self, name)
|
|
24
|
+
else # emits by a name
|
|
25
|
+
event = Event.new(nil, self, *event)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
UnderOs::Events::Listeners.kick(self, event, {})
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def on=(hash)
|
|
32
|
+
on hash
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class TouchListeners
|
|
36
|
+
def self.listeners
|
|
37
|
+
@listeners ||= Hash.new{ |h,k| h[k] = [] }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.add(eventname, view)
|
|
41
|
+
listeners[eventname] << view
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.notify(eventname, event)
|
|
45
|
+
listeners[eventname].each do |view|
|
|
46
|
+
if eventname == :touchmove # being nice and throttling the touchmove events
|
|
47
|
+
return if @__working
|
|
48
|
+
@__working = true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
touches = touches_for_view(view, event)
|
|
52
|
+
|
|
53
|
+
view.emit(eventname, touches: touches) if touches.size > 0
|
|
54
|
+
|
|
55
|
+
@__working = false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.touches_for_view(view, event)
|
|
60
|
+
frame = view._.frame
|
|
61
|
+
touches = []
|
|
62
|
+
|
|
63
|
+
event.allTouches.each do |touch|
|
|
64
|
+
if point = touch_inside_of(frame, touch)
|
|
65
|
+
touches << Touch.new(view, point)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
touches
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.touch_inside_of(frame, touch)
|
|
73
|
+
point = touch.locationInView(nil)
|
|
74
|
+
point = nil if point.x < frame.origin.x ||
|
|
75
|
+
point.y < frame.origin.y ||
|
|
76
|
+
point.x > frame.origin.x + frame.size.width ||
|
|
77
|
+
point.y > frame.origin.y + frame.size.height
|
|
78
|
+
|
|
79
|
+
point
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class Touch
|
|
83
|
+
attr_reader :view, :position
|
|
84
|
+
|
|
85
|
+
def initialize(view, position)
|
|
86
|
+
@view = view
|
|
87
|
+
@position = position
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def pageX
|
|
91
|
+
position.x
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def pageY
|
|
95
|
+
position.y
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def viewX
|
|
99
|
+
@position.x - view._.frame.origin.x
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def viewY
|
|
103
|
+
@position.y - view._.frame.origin.y
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def inspect
|
|
107
|
+
"#<Touch x=#{pageX} y=#{pageY}"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def add_ui_event_listener(event)
|
|
115
|
+
event = event.to_sym if event.is_a?(String)
|
|
116
|
+
event = try_add_touch_event_listener(event)
|
|
117
|
+
event, recognizer = find_recognizer_from(event)
|
|
118
|
+
|
|
119
|
+
@_.addGestureRecognizer(recognizer.alloc.initWithTarget(self, action: :emit)) if recognizer
|
|
120
|
+
|
|
121
|
+
event
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
TOUCH_EVENTS = [:touchstart, :touchmove, :touchend, :touchcancel]
|
|
125
|
+
|
|
126
|
+
def try_add_touch_event_listener(event)
|
|
127
|
+
if TOUCH_EVENTS.include?(event)
|
|
128
|
+
UnderOs::UI::Events::TouchListeners.add(event, self)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
event
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
RECOGNIZERS = {
|
|
135
|
+
tap: UITapGestureRecognizer,
|
|
136
|
+
pinch: UIPinchGestureRecognizer,
|
|
137
|
+
rotate: UIRotationGestureRecognizer,
|
|
138
|
+
swipe: UISwipeGestureRecognizer,
|
|
139
|
+
pan: UIPanGestureRecognizer,
|
|
140
|
+
press: UILongPressGestureRecognizer
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# tries to figure event name and gesture recognizer
|
|
144
|
+
def find_recognizer_from(event)
|
|
145
|
+
if event.is_a?(Class) && event < UIGestureRecognizer
|
|
146
|
+
recognizer = event
|
|
147
|
+
|
|
148
|
+
if recognizer.respond_to?(:event_name)
|
|
149
|
+
event = recognizer.event_name
|
|
150
|
+
else
|
|
151
|
+
RECOGNIZERS.each{ |e,r| event = e if r == recognizer }
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
elsif RECOGNIZERS[event]
|
|
155
|
+
recognizer = RECOGNIZERS[event]
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
[event, recognizer]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
class Event < UnderOs::Events::Event
|
|
162
|
+
attr_reader :_, :target
|
|
163
|
+
|
|
164
|
+
def initialize(original, target, event, params={})
|
|
165
|
+
@_ = original
|
|
166
|
+
@target = target
|
|
167
|
+
|
|
168
|
+
super event, params
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def scale
|
|
172
|
+
@_.scale
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def scale=(value)
|
|
176
|
+
@_.scale = value
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def angle
|
|
180
|
+
@_.rotation
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def angle=(value)
|
|
184
|
+
@_.rotation = value
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def translation(view)
|
|
188
|
+
view = view._ if view.is_a?(UnderOs::UI::View)
|
|
189
|
+
@_.translationInView(view)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def translation=(params)
|
|
193
|
+
point = params[0]; view = params[1]
|
|
194
|
+
view = view._ if view.is_a?(UnderOs::UI::View)
|
|
195
|
+
@_.setTranslation(point, inView:view)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
RECOGNIZER_STATES = {
|
|
199
|
+
UIGestureRecognizerStateBegan => :began,
|
|
200
|
+
UIGestureRecognizerStateChanged => :changed,
|
|
201
|
+
UIGestureRecognizerStateEnded => :ended,
|
|
202
|
+
UIGestureRecognizerStateCancelled => :canceled,
|
|
203
|
+
UIGestureRecognizerStateFailed => :failed
|
|
204
|
+
}.freeze
|
|
205
|
+
|
|
206
|
+
def state
|
|
207
|
+
RECOGNIZER_STATES[@_.state]
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|