motion-xray 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +21 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +28 -0
- data/README.md +426 -0
- data/Rakefile +11 -0
- data/app/app_delegate.rb +44 -0
- data/lib/motion-xray.rb +37 -0
- data/lib/motion-xray/plugins/accessibility_plugin.rb +129 -0
- data/lib/motion-xray/plugins/log_plugin.rb +301 -0
- data/lib/motion-xray/plugins/save_ui_plugin.rb +142 -0
- data/lib/motion-xray/plugins/ui_plugin.rb +41 -0
- data/lib/motion-xray/version.rb +5 -0
- data/lib/motion-xray/views/xray_color_swatch.rb +234 -0
- data/lib/motion-xray/views/xray_dpad.rb +142 -0
- data/lib/motion-xray/views/xray_gradient_view.rb +23 -0
- data/lib/motion-xray/views/xray_headers.rb +101 -0
- data/lib/motion-xray/views/xray_lock_button.rb +50 -0
- data/lib/motion-xray/views/xray_scroll_view.rb +12 -0
- data/lib/motion-xray/views/xray_toolbar.rb +173 -0
- data/lib/motion-xray/views/xray_window.rb +13 -0
- data/lib/motion-xray/xray.rb +56 -0
- data/lib/motion-xray/xray_constants.rb +5 -0
- data/lib/motion-xray/xray_dummy.rb +8 -0
- data/lib/motion-xray/xray_editors.rb +62 -0
- data/lib/motion-xray/xray_ext.rb +125 -0
- data/lib/motion-xray/xray_plugin.rb +40 -0
- data/lib/motion-xray/xray_typewriter.rb +217 -0
- data/lib/motion-xray/xray_ui.rb +723 -0
- data/lib/motion-xray/z_editors/xray_boolean_editor.rb +24 -0
- data/lib/motion-xray/z_editors/xray_color_editor.rb +119 -0
- data/lib/motion-xray/z_editors/xray_frame_editor.rb +108 -0
- data/lib/motion-xray/z_editors/xray_image_editor.rb +78 -0
- data/lib/motion-xray/z_editors/xray_text_editor.rb +94 -0
- data/lib/resources/xray_button_bg@2x.png +0 -0
- data/lib/resources/xray_choose_button@2x.png +0 -0
- data/lib/resources/xray_clear_button@2x.png +0 -0
- data/lib/resources/xray_detail_button@2x.png +0 -0
- data/lib/resources/xray_dpad@2x.png +0 -0
- data/lib/resources/xray_dpad_center@2x.png +0 -0
- data/lib/resources/xray_dpad_down@2x.png +0 -0
- data/lib/resources/xray_dpad_left@2x.png +0 -0
- data/lib/resources/xray_dpad_right@2x.png +0 -0
- data/lib/resources/xray_dpad_up@2x.png +0 -0
- data/lib/resources/xray_drawer_left@2x.png +0 -0
- data/lib/resources/xray_drawer_right@2x.png +0 -0
- data/lib/resources/xray_edit_button@2x.png +0 -0
- data/lib/resources/xray_email_button@2x.png +0 -0
- data/lib/resources/xray_lock_button_horizontal@2x.png +0 -0
- data/lib/resources/xray_lock_button_locked@2x.png +0 -0
- data/lib/resources/xray_lock_button_unlocked@2x.png +0 -0
- data/lib/resources/xray_lock_button_vertical@2x.png +0 -0
- data/motion-xray.gemspec +40 -0
- data/resources/Default-568h@2x.png +0 -0
- data/spec/xray_view_spec.rb +43 -0
- data/vendor/Podfile.lock +11 -0
- metadata +177 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
module Motion ; module Xray
|
2
|
+
|
3
|
+
class Editor
|
4
|
+
attr_accessor :target
|
5
|
+
attr_accessor :property
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def with_target(target, property:property)
|
9
|
+
self.new(target, property)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(target, property)
|
14
|
+
@target = target
|
15
|
+
@property = property
|
16
|
+
end
|
17
|
+
|
18
|
+
def did_change?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_edit_view(container_width)
|
23
|
+
@edit_view ||= self.edit_view(container_width)
|
24
|
+
end
|
25
|
+
|
26
|
+
def did_change?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class PropertyEditor < Editor
|
33
|
+
|
34
|
+
def initialize(target, property)
|
35
|
+
super
|
36
|
+
@original = get_value
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_value
|
40
|
+
if target.respond_to?(property)
|
41
|
+
return target.send(property)
|
42
|
+
elsif target.respond_to?("#{property}?")
|
43
|
+
value = target.send("#{property}?")
|
44
|
+
return target.send("#{property}?")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_value(value)
|
49
|
+
assign = "#{property}="
|
50
|
+
setter = "set#{property.sub(/^./) { |c| c.upcase }}"
|
51
|
+
|
52
|
+
if target.respond_to?(assign)
|
53
|
+
target.send(assign, value)
|
54
|
+
elsif target.respond_to?(setter)
|
55
|
+
target.send(setter, value)
|
56
|
+
end
|
57
|
+
XrayTargetDidChangeNotification.post_notification(@target, { 'property' => @property, 'value' => value, 'original' => @original })
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
class UIImage
|
2
|
+
class << self
|
3
|
+
alias :imageNamed_xray_old :imageNamed
|
4
|
+
def imageNamed(name)
|
5
|
+
imageNamed_xray_old(name)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class UIView
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :xray
|
14
|
+
|
15
|
+
def xray
|
16
|
+
{
|
17
|
+
'Frame' => {
|
18
|
+
frame: Motion::Xray::FrameEditor,
|
19
|
+
},
|
20
|
+
'Color' => {
|
21
|
+
backgroundColor: Motion::Xray::ColorEditor,
|
22
|
+
},
|
23
|
+
'UI' => {
|
24
|
+
hidden: Motion::Xray::BooleanEditor,
|
25
|
+
userInteractionEnabled: Motion::Xray::BooleanEditor,
|
26
|
+
accessibilityLabel: Motion::Xray::TextEditor,
|
27
|
+
},
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
# this could be optimized a tiny bit by only calling superclass.build_xray
|
32
|
+
# but i am le tired
|
33
|
+
def build_xray
|
34
|
+
@build_xray ||= begin
|
35
|
+
retval = Hash.new { |hash,key| hash[key] = {} }
|
36
|
+
klasses = []
|
37
|
+
klass = self
|
38
|
+
while klass && klass <= UIView
|
39
|
+
klasses.unshift(klass)
|
40
|
+
klass = klass.superclass
|
41
|
+
end
|
42
|
+
|
43
|
+
klasses.each do |klass|
|
44
|
+
xray_props = klass.xray
|
45
|
+
xray_props && xray_props.each do |key,values|
|
46
|
+
values.keys.each do |check_unique|
|
47
|
+
retval.each do |section, editors|
|
48
|
+
editors.delete(check_unique)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
retval[key].merge!(values)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# clean out nil-editors and empty sections
|
56
|
+
retval.each do |section, editors|
|
57
|
+
editors.each do |property, editor|
|
58
|
+
editors.delete(property) unless editor
|
59
|
+
end
|
60
|
+
retval.delete(section) if editors.length == 0
|
61
|
+
end
|
62
|
+
|
63
|
+
retval
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
def xray
|
70
|
+
self.class.build_xray
|
71
|
+
end
|
72
|
+
|
73
|
+
def xray_subviews
|
74
|
+
subviews
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
class << UIWindow
|
80
|
+
def xray
|
81
|
+
{
|
82
|
+
'TurnOff' => {
|
83
|
+
frame: nil,
|
84
|
+
hidden: nil,
|
85
|
+
userInteractionEnabled: nil,
|
86
|
+
},
|
87
|
+
}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class << UILabel
|
92
|
+
def xray
|
93
|
+
{
|
94
|
+
'Content' => {
|
95
|
+
text: Motion::Xray::TextEditor,
|
96
|
+
}
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class << UITabBar
|
102
|
+
def xray
|
103
|
+
{
|
104
|
+
'Color' => {
|
105
|
+
tintColor: Motion::Xray::ColorEditor,
|
106
|
+
},
|
107
|
+
}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class << UINavigationBar
|
112
|
+
def xray
|
113
|
+
{
|
114
|
+
'Color' => {
|
115
|
+
tintColor: Motion::Xray::ColorEditor,
|
116
|
+
},
|
117
|
+
}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class UIButton
|
122
|
+
def xray_subviews
|
123
|
+
[]
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Motion ; module Xray
|
2
|
+
|
3
|
+
class Plugin
|
4
|
+
attr_accessor :name
|
5
|
+
attr :view
|
6
|
+
attr :target
|
7
|
+
|
8
|
+
def Plugin.name(value=nil)
|
9
|
+
if value
|
10
|
+
@name = value
|
11
|
+
else
|
12
|
+
@name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def xray_name
|
17
|
+
@name || self.class.name
|
18
|
+
end
|
19
|
+
|
20
|
+
def plugin_view(canvas)
|
21
|
+
raise "You must implement `#{self.class}#plugin_view`"
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_plugin_view(canvas)
|
25
|
+
@view ||= plugin_view(canvas)
|
26
|
+
end
|
27
|
+
|
28
|
+
def edit(target)
|
29
|
+
@target = target
|
30
|
+
end
|
31
|
+
|
32
|
+
def show
|
33
|
+
end
|
34
|
+
|
35
|
+
def hide
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
module Motion ; module Xray
|
2
|
+
# Copied from the gem of the same name, but I didn't want the dependency
|
3
|
+
class XrayTypewriterView < UIView
|
4
|
+
attr_accessor :scroll_view
|
5
|
+
attr_accessor :background_view
|
6
|
+
|
7
|
+
attr_accessor :vertical_spacing, :horizontal_spacing
|
8
|
+
attr_accessor :top_margin, :bottom_margin
|
9
|
+
attr_accessor :left_margin, :right_margin
|
10
|
+
|
11
|
+
attr_accessor :centered
|
12
|
+
attr_accessor :min_width, :min_height
|
13
|
+
|
14
|
+
def initWithFrame(frame)
|
15
|
+
super.tap do
|
16
|
+
self.spacing = 0
|
17
|
+
self.margin = 0
|
18
|
+
self.centered = false
|
19
|
+
self.min_width = nil
|
20
|
+
self.min_height = nil
|
21
|
+
self.contentMode = :bottom.uicontentmode
|
22
|
+
|
23
|
+
@row = []
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def shrink
|
28
|
+
self.frame = self.frame.height(0)
|
29
|
+
end
|
30
|
+
|
31
|
+
def expand
|
32
|
+
self.frame = self.frame.height(self.subviews[0].frame.height)
|
33
|
+
end
|
34
|
+
|
35
|
+
def spacing=(spacing)
|
36
|
+
if spacing.is_a? Array
|
37
|
+
case spacing.length
|
38
|
+
when 1
|
39
|
+
self.spacing = spacing[0]
|
40
|
+
when 2
|
41
|
+
@horizontal_spacing = spacing[0]
|
42
|
+
@vertical_spacing = spacing[1]
|
43
|
+
end
|
44
|
+
else
|
45
|
+
@horizontal_spacing = @vertical_spacing = spacing
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def margin=(margins)
|
50
|
+
margins = [margins] unless margins.is_a? Enumerable
|
51
|
+
|
52
|
+
case margins.length
|
53
|
+
when 1
|
54
|
+
@top_margin = @bottom_margin = @left_margin = @right_margin = margins[0]
|
55
|
+
when 2
|
56
|
+
@top_margin = margins[0]
|
57
|
+
@bottom_margin = margins[0]
|
58
|
+
@left_margin = margins[1]
|
59
|
+
@right_margin = margins[1]
|
60
|
+
when 3
|
61
|
+
@top_margin = margins[0]
|
62
|
+
@bottom_margin = margins[0]
|
63
|
+
@right_margin = margins[1]
|
64
|
+
@left_margin = margins[2]
|
65
|
+
when 4
|
66
|
+
@top_margin = margins[0]
|
67
|
+
@right_margin = margins[1]
|
68
|
+
@bottom_margin = margins[2]
|
69
|
+
@left_margin = margins[3]
|
70
|
+
else
|
71
|
+
raise "Too many arguments (#{margins.length}) sent to MarginView#margin"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
##| DEFAULTS
|
76
|
+
def vertical_spacing
|
77
|
+
@vertical_spacing ||= 0
|
78
|
+
@vertical_spacing
|
79
|
+
end
|
80
|
+
def horizontal_spacing
|
81
|
+
@horizontal_spacing ||= 0
|
82
|
+
@horizontal_spacing
|
83
|
+
end
|
84
|
+
def top_margin
|
85
|
+
@top_margin ||= 0
|
86
|
+
@top_margin
|
87
|
+
end
|
88
|
+
def bottom_margin
|
89
|
+
@bottom_margin ||= 0
|
90
|
+
@bottom_margin
|
91
|
+
end
|
92
|
+
def left_margin
|
93
|
+
@left_margin ||= 0
|
94
|
+
@left_margin
|
95
|
+
end
|
96
|
+
def right_margin
|
97
|
+
@right_margin ||= 0
|
98
|
+
@right_margin
|
99
|
+
end
|
100
|
+
|
101
|
+
##|
|
102
|
+
##| START AT 0, 0, AND START FLOATING
|
103
|
+
##|
|
104
|
+
def layoutSubviews
|
105
|
+
super
|
106
|
+
# super
|
107
|
+
# the max_height of *all* the rows so far (not just the current row)
|
108
|
+
@max_height = top_margin
|
109
|
+
clear
|
110
|
+
|
111
|
+
# when a row would be longer than this, it is wrapped to the next row.
|
112
|
+
@max_x = self.frame.size.width - right_margin
|
113
|
+
|
114
|
+
self.subviews.each do |view|
|
115
|
+
unless view == @background_view
|
116
|
+
view.setNeedsLayout
|
117
|
+
view.layoutIfNeeded
|
118
|
+
add_next(view)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
clear(true) # don't add vertical spacing
|
122
|
+
|
123
|
+
self.frame = [self.frame.origin, [self.frame.size.width, @y + bottom_margin]]
|
124
|
+
if scroll_view
|
125
|
+
scroll_view.scrollEnabled = (@y > scroll_view.frame.size.height)
|
126
|
+
scroll_view.contentSize = self.frame.size
|
127
|
+
end
|
128
|
+
if background_view
|
129
|
+
background_view.frame = self.bounds
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def setNeedsLayout
|
134
|
+
super
|
135
|
+
end
|
136
|
+
|
137
|
+
def background_view=(view)
|
138
|
+
@background_view.removeFromSuperview if @background_view && @background_view.superview == self
|
139
|
+
view.frame = self.bounds
|
140
|
+
self.insertSubview(view, atIndex:0)
|
141
|
+
@background_view = view
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
def clear(is_last_row=false)
|
146
|
+
@x = left_margin
|
147
|
+
@y = @max_height
|
148
|
+
# only add the horizontal_spacing after at least one row has been written
|
149
|
+
if @y > top_margin && ! is_last_row
|
150
|
+
@y += vertical_spacing
|
151
|
+
end
|
152
|
+
|
153
|
+
if self.centered
|
154
|
+
row_width = left_margin
|
155
|
+
row_height = min_height || 0
|
156
|
+
@row.each do |view|
|
157
|
+
if row_width > 0
|
158
|
+
row_width += horizontal_spacing
|
159
|
+
end
|
160
|
+
row_height = view.frame.size.height if view.frame.size.height > row_height
|
161
|
+
row_width += view.frame.size.width
|
162
|
+
end
|
163
|
+
row_width += right_margin
|
164
|
+
row_width = min_width if min_width and row_width < min_width
|
165
|
+
x = ((self.frame.size.width - row_width) / 2).round
|
166
|
+
|
167
|
+
@row.each do |view|
|
168
|
+
frame = view.frame
|
169
|
+
|
170
|
+
y = ((row_height - frame.size.height) / 2).round
|
171
|
+
|
172
|
+
if x > 0 or y > 0
|
173
|
+
frame.origin.x += x
|
174
|
+
frame.origin.y += y
|
175
|
+
view.frame = frame
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
@row = []
|
180
|
+
end
|
181
|
+
|
182
|
+
def add_next(subview)
|
183
|
+
# this frame will get modified and reassigned
|
184
|
+
subview_frame = subview.frame
|
185
|
+
|
186
|
+
# move to the next new row?
|
187
|
+
width = subview_frame.size.width
|
188
|
+
if self.min_width and width < self.min_width
|
189
|
+
width = self.min_width
|
190
|
+
end
|
191
|
+
|
192
|
+
next_x = @x + width
|
193
|
+
# too big?
|
194
|
+
if next_x > @max_x
|
195
|
+
clear
|
196
|
+
next_x = @x + width
|
197
|
+
end
|
198
|
+
|
199
|
+
# new max_height?
|
200
|
+
height = subview_frame.size.height
|
201
|
+
if self.min_height and height < self.min_height
|
202
|
+
height = self.min_height
|
203
|
+
end
|
204
|
+
next_y = @y + height
|
205
|
+
@max_height = next_y if next_y > @max_height
|
206
|
+
|
207
|
+
subview_frame.origin.x = @x
|
208
|
+
subview_frame.origin.y = @y
|
209
|
+
@x = next_x + horizontal_spacing
|
210
|
+
|
211
|
+
subview.frame = subview_frame
|
212
|
+
@row.push subview
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
end end
|
@@ -0,0 +1,723 @@
|
|
1
|
+
module Motion ; module Xray
|
2
|
+
|
3
|
+
class XrayViewController < UIViewController
|
4
|
+
|
5
|
+
def loadView
|
6
|
+
self.view = Xray.ui.teh_ui
|
7
|
+
end
|
8
|
+
|
9
|
+
def supportedInterfaceOrientations
|
10
|
+
UIInterfaceOrientationPortrait
|
11
|
+
end
|
12
|
+
|
13
|
+
def shouldAutorotate
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def willAnimateRotationToInterfaceOrientation(orientation, duration:duration)
|
18
|
+
Xray.ui.update_orientation
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class UI
|
24
|
+
attr :teh_ui
|
25
|
+
attr :tiny_view
|
26
|
+
attr :expand_button
|
27
|
+
attr :assign_button
|
28
|
+
attr :top_bar
|
29
|
+
attr :bottom_bar
|
30
|
+
attr :bottom_half
|
31
|
+
attr :canvas
|
32
|
+
attr :label
|
33
|
+
attr :revert
|
34
|
+
attr :selected
|
35
|
+
attr :target
|
36
|
+
attr :cover
|
37
|
+
attr :selector
|
38
|
+
attr :subviews
|
39
|
+
attr :superview
|
40
|
+
attr :table
|
41
|
+
attr :table_source
|
42
|
+
|
43
|
+
def toggle
|
44
|
+
if fired?
|
45
|
+
cool_down
|
46
|
+
else
|
47
|
+
fire_up
|
48
|
+
end
|
49
|
+
return fired?
|
50
|
+
end
|
51
|
+
|
52
|
+
def full_screen_width
|
53
|
+
Xray.window.bounds.width
|
54
|
+
end
|
55
|
+
|
56
|
+
def full_screen_height
|
57
|
+
Xray.window.bounds.height
|
58
|
+
end
|
59
|
+
|
60
|
+
def half_screen_width
|
61
|
+
full_screen_width / 2
|
62
|
+
end
|
63
|
+
|
64
|
+
def half_screen_height
|
65
|
+
Xray.window.bounds.height / 2
|
66
|
+
end
|
67
|
+
|
68
|
+
def bottom_half_height
|
69
|
+
full_screen_height - half_screen_height
|
70
|
+
end
|
71
|
+
|
72
|
+
def bar_height
|
73
|
+
30
|
74
|
+
end
|
75
|
+
|
76
|
+
def bottom_half_top
|
77
|
+
half_screen_height
|
78
|
+
end
|
79
|
+
|
80
|
+
def toolbar_top
|
81
|
+
bottom_half_height - 25
|
82
|
+
end
|
83
|
+
|
84
|
+
def toolbar_height
|
85
|
+
25
|
86
|
+
end
|
87
|
+
|
88
|
+
def canvas_top
|
89
|
+
0
|
90
|
+
end
|
91
|
+
|
92
|
+
def canvas_height
|
93
|
+
bottom_half_height - toolbar_height
|
94
|
+
end
|
95
|
+
|
96
|
+
def fired?
|
97
|
+
@fired
|
98
|
+
end
|
99
|
+
|
100
|
+
def build_the_ui
|
101
|
+
unless @teh_ui
|
102
|
+
@teh_ui = UIView.alloc.initWithFrame(Xray.window.bounds)
|
103
|
+
@xray_controller = Xray.controller
|
104
|
+
|
105
|
+
@tiny_view = UIView.alloc.initWithFrame([[0, 0], [half_screen_width, half_screen_height]])
|
106
|
+
@teh_ui << @tiny_view
|
107
|
+
|
108
|
+
@cover = UIControl.alloc.initWithFrame([[0, 0], [half_screen_width, half_screen_height]])
|
109
|
+
@cover.on :touch {
|
110
|
+
Xray.cool_down
|
111
|
+
2.seconds.later do
|
112
|
+
Xray.fire_up
|
113
|
+
end
|
114
|
+
}
|
115
|
+
@teh_ui << @cover
|
116
|
+
|
117
|
+
@selector = UIView.alloc.init
|
118
|
+
@selector.userInteractionEnabled = false
|
119
|
+
@selector.layer.borderWidth = 1
|
120
|
+
@selector.layer.borderColor = '#a91105'.uicolor.cgcolor
|
121
|
+
@selector.layer.opacity = 0
|
122
|
+
@teh_ui << @selector
|
123
|
+
|
124
|
+
@top_half = UIView.alloc.initWithFrame([[half_screen_width, 0], [half_screen_width, half_screen_height]])
|
125
|
+
@teh_ui << @top_half
|
126
|
+
|
127
|
+
@table = UITableView.alloc.initWithFrame(CGRect.empty, style: :plain.uitableviewstyle)
|
128
|
+
@table.frame = [[0, bar_height], [half_screen_width, half_screen_height - bar_height * 2]]
|
129
|
+
@table.rowHeight = 30
|
130
|
+
@table.delegate = self
|
131
|
+
@table.autoresizingMask = :full.uiautoresizemask
|
132
|
+
@top_half << @table
|
133
|
+
|
134
|
+
@top_bar = XrayHeaderBackground.alloc.initWithFrame([[0, 0], [half_screen_width, bar_height]])
|
135
|
+
@top_bar.autoresizingMask = :fixed_top.uiautoresizemask
|
136
|
+
@top_bar.label = XrayHeaderLabel.alloc.initWithFrame(@top_bar.bounds.right(30).thinner(30))
|
137
|
+
@top_bar.label.autoresizingMask = :flexible_width.uiautoresizemask
|
138
|
+
|
139
|
+
@expand_button = XrayDetailButton.alloc.init
|
140
|
+
@expand_button.transform = CGAffineTransformMakeRotation(180.degrees)
|
141
|
+
@expand_button.on :touch {
|
142
|
+
toggle_picker
|
143
|
+
}
|
144
|
+
@top_bar << @expand_button
|
145
|
+
|
146
|
+
@choose_button = UIButton.custom
|
147
|
+
@choose_button.setImage('xray_choose_button'.uiimage, forState: :normal.uicontrolstate)
|
148
|
+
@choose_button.sizeToFit
|
149
|
+
f = @choose_button.frame
|
150
|
+
f.origin = @top_bar.bounds.top_right + CGPoint.new(-4 - f.width, 4)
|
151
|
+
@choose_button.frame = f
|
152
|
+
@choose_button.on :touch {
|
153
|
+
choose_view
|
154
|
+
}
|
155
|
+
@choose_button.autoresizingMask = :fixed_top_right.uiautoresizemask
|
156
|
+
@top_bar << @choose_button
|
157
|
+
|
158
|
+
@top_half << @top_bar
|
159
|
+
|
160
|
+
@bottom_bar = XrayHeaderBackground.alloc.initWithFrame([[0, half_screen_height - bar_height], [half_screen_width, bar_height]])
|
161
|
+
@bottom_bar.label = XrayHeaderLabel.alloc.initWithFrame(@bottom_bar.bounds.right(3).thinner(33))
|
162
|
+
@bottom_bar.label.autoresizingMask = :flexible_width.uiautoresizemask
|
163
|
+
@bottom_bar.autoresizingMask = :fixed_bottom.uiautoresizemask
|
164
|
+
@top_half << @bottom_bar
|
165
|
+
|
166
|
+
@assign_button = XrayDetailButton.alloc.init
|
167
|
+
@assign_button.transform = CGAffineTransformMakeRotation(90.degrees)
|
168
|
+
@assign_button.frame = @assign_button.frame.x(half_screen_width - @assign_button.frame.width)
|
169
|
+
@assign_button.on :touch {
|
170
|
+
edit(@selected) if @selected
|
171
|
+
}
|
172
|
+
@assign_button.autoresizingMask = :fixed_bottom_right.uiautoresizemask
|
173
|
+
@bottom_bar << @assign_button
|
174
|
+
|
175
|
+
@bottom_half = UIView.alloc.initWithFrame([[0, bottom_half_top], [full_screen_width, bottom_half_height]])
|
176
|
+
grad_layer = CAGradientLayer.layer
|
177
|
+
grad_layer.frame = @bottom_half.layer.bounds
|
178
|
+
grad_layer.colors = [:white.uicolor.cgcolor, :lightgray.uicolor.cgcolor]
|
179
|
+
@bottom_half.layer << grad_layer
|
180
|
+
@teh_ui << @bottom_half
|
181
|
+
|
182
|
+
@canvas = XrayScrollView.alloc.init
|
183
|
+
@canvas.frame = [[0, canvas_top], [full_screen_width, canvas_height]]
|
184
|
+
@bottom_half << @canvas
|
185
|
+
|
186
|
+
@toolbar = PluginToolbar.alloc.initWithFrame([[-1, toolbar_top], [full_screen_width + 2, toolbar_height + 1]])
|
187
|
+
@toolbar.canvas = @canvas
|
188
|
+
@bottom_half << @toolbar
|
189
|
+
|
190
|
+
Xray.plugins.each do |plugin|
|
191
|
+
@toolbar.add(plugin)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
@tiny_view.subviews.each &:removeFromSuperview
|
196
|
+
end
|
197
|
+
|
198
|
+
def transition_ui
|
199
|
+
@selector.fade_in
|
200
|
+
|
201
|
+
@top_half.frame = @top_half.frame.x(full_screen_width)
|
202
|
+
@top_half.slide :left, half_screen_width
|
203
|
+
|
204
|
+
@bottom_half.frame = @bottom_half.frame.y(full_screen_height)
|
205
|
+
@bottom_half.slide :up, bottom_half_height
|
206
|
+
end
|
207
|
+
|
208
|
+
def fire_up
|
209
|
+
return if @fired
|
210
|
+
@fired = true
|
211
|
+
Xray.window.first_responder && Xray.window.first_responder.resignFirstResponder
|
212
|
+
|
213
|
+
# gather all window subviews into 'revert_view'
|
214
|
+
@revert = {
|
215
|
+
views: [],
|
216
|
+
status_bar_was_hidden?: Xray.app_shared.statusBarHidden?,
|
217
|
+
transforms: {}
|
218
|
+
}
|
219
|
+
|
220
|
+
Xray.window.subviews.each do |subview|
|
221
|
+
@revert[:views] << subview
|
222
|
+
@revert[:transforms][subview] = subview.layer.transform
|
223
|
+
end
|
224
|
+
Xray.app_shared.setStatusBarHidden(true, withAnimation:UIStatusBarAnimationSlide)
|
225
|
+
|
226
|
+
build_the_ui
|
227
|
+
@old_controller = Xray.window.rootViewController
|
228
|
+
Xray.window.rootViewController = @xray_controller
|
229
|
+
@revert[:views].each do |view|
|
230
|
+
@tiny_view << view
|
231
|
+
end
|
232
|
+
|
233
|
+
transition_ui
|
234
|
+
apply_transform(true)
|
235
|
+
|
236
|
+
if @selected && ! @selected.isDescendantOfView(Xray.window)
|
237
|
+
@selected = nil
|
238
|
+
end
|
239
|
+
if @target && ! @target.isDescendantOfView(Xray.window)
|
240
|
+
@target = nil
|
241
|
+
end
|
242
|
+
|
243
|
+
subviews = view_tree
|
244
|
+
@table_source = XrayTableSource.new(@selected || Xray.window, subviews)
|
245
|
+
@table.dataSource = @table_source
|
246
|
+
@table.delegate = self
|
247
|
+
|
248
|
+
select(Xray.window) unless @selected
|
249
|
+
edit(Xray.window) unless @target
|
250
|
+
end
|
251
|
+
|
252
|
+
def cool_down
|
253
|
+
return unless @fired
|
254
|
+
@fired = false
|
255
|
+
|
256
|
+
@selector.fade_out
|
257
|
+
@top_half.slide(:right, half_screen_width)
|
258
|
+
@bottom_half.slide(:down, bottom_half_height) {
|
259
|
+
Xray.window.rootViewController = @old_controller
|
260
|
+
@old_controller = nil
|
261
|
+
|
262
|
+
@teh_ui.removeFromSuperview
|
263
|
+
}
|
264
|
+
|
265
|
+
@revert[:views].each do |subview|
|
266
|
+
Xray.window << subview
|
267
|
+
UIView.animate {
|
268
|
+
# identity matrix
|
269
|
+
subview.layer.transform = @revert[:transforms][subview]
|
270
|
+
subview.layer.anchorPoint = [0.5, 0.5]
|
271
|
+
}
|
272
|
+
end
|
273
|
+
Xray.app_shared.setStatusBarHidden(@revert[:status_bar_was_hidden?], withAnimation:UIStatusBarAnimationSlide)
|
274
|
+
@revert = nil
|
275
|
+
end
|
276
|
+
|
277
|
+
def view_tree(view=nil, indent=nil, depth=0, is_last=true, return_views=[])
|
278
|
+
if view
|
279
|
+
subviews = view.xray_subviews
|
280
|
+
else
|
281
|
+
view = Xray.window
|
282
|
+
subviews = @revert[:views]
|
283
|
+
end
|
284
|
+
|
285
|
+
if indent
|
286
|
+
next_indent = indent.dup
|
287
|
+
if is_last
|
288
|
+
indent += "╘═ "
|
289
|
+
next_indent += ' '
|
290
|
+
else
|
291
|
+
indent += "╞═ "
|
292
|
+
next_indent += "┃ "
|
293
|
+
end
|
294
|
+
else
|
295
|
+
indent = ''
|
296
|
+
next_indent = ''
|
297
|
+
end
|
298
|
+
|
299
|
+
return_views << {view: view, indent: indent, depth: depth}
|
300
|
+
|
301
|
+
subviews.each_with_index { |subview, index|
|
302
|
+
view_tree(subview, next_indent, depth + 1, index == subviews.length - 1, return_views)
|
303
|
+
}
|
304
|
+
return return_views
|
305
|
+
end
|
306
|
+
|
307
|
+
def choose_view
|
308
|
+
restore_shown_views = collect_visible_views
|
309
|
+
|
310
|
+
@choose_view = UIView.alloc.initWithFrame(Xray.window.bounds)
|
311
|
+
@choose_view.backgroundColor = :black.uicolor
|
312
|
+
@choose_view.opaque = true
|
313
|
+
@choose_view.alpha = 0.0
|
314
|
+
|
315
|
+
controls = @revert[:views].reverse.map { |subview|
|
316
|
+
buttony_views(subview)
|
317
|
+
}.flatten
|
318
|
+
|
319
|
+
restore_shown_views.each do |subview|
|
320
|
+
subview.show
|
321
|
+
end
|
322
|
+
|
323
|
+
label = UILabel.alloc.initWithFrame([[5, 5], [0, 0]])
|
324
|
+
label.backgroundColor = :clear.uicolor
|
325
|
+
label.textColor = :white.uicolor
|
326
|
+
label.textAlignment = :left.uitextalignment
|
327
|
+
|
328
|
+
container = UIView.alloc.initWithFrame(CGRect.empty)
|
329
|
+
container.layer.cornerRadius = label.frame.height/2
|
330
|
+
container.backgroundColor = :black.uicolor(0.5)
|
331
|
+
container << label
|
332
|
+
|
333
|
+
controls.reverse.each do |control|
|
334
|
+
control.container = container
|
335
|
+
control.label = label
|
336
|
+
@choose_view << control
|
337
|
+
end
|
338
|
+
|
339
|
+
@choose_view << container
|
340
|
+
|
341
|
+
Xray.window << @choose_view
|
342
|
+
@choose_view.fade_in
|
343
|
+
timer = 0
|
344
|
+
end
|
345
|
+
|
346
|
+
def did_choose_view(view)
|
347
|
+
radius = Math.sqrt(Xray.window.bounds.width**2 + Xray.window.bounds.height**2)
|
348
|
+
window_center = Xray.window.center
|
349
|
+
@choose_view.subviews.each do |subview|
|
350
|
+
angle = window_center.angle_to(subview.center)
|
351
|
+
random_x = radius * Math.cos(angle)
|
352
|
+
random_y = radius * Math.sin(angle)
|
353
|
+
subview.move_to([random_x, random_y], 1)
|
354
|
+
end
|
355
|
+
0.5.later do
|
356
|
+
@choose_view.fade_out_and_remove
|
357
|
+
end
|
358
|
+
edit(view)
|
359
|
+
select(view)
|
360
|
+
end
|
361
|
+
|
362
|
+
def collect_visible_views(view=nil)
|
363
|
+
if view
|
364
|
+
# join all the subviews
|
365
|
+
view.xray_subviews.reverse.map { |subview|
|
366
|
+
collect_visible_views(subview)
|
367
|
+
}.flatten + [view]
|
368
|
+
else
|
369
|
+
# start at the revert[:views] and collect all subviews
|
370
|
+
@revert[:views].reverse.map { |subview|
|
371
|
+
collect_visible_views(subview)
|
372
|
+
}.flatten.select { |subview| !subview.hidden? }
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
def buttony_views(view)
|
377
|
+
children = view.xray_subviews.reverse.map { |subview|
|
378
|
+
buttony_views(subview)
|
379
|
+
}.flatten
|
380
|
+
|
381
|
+
f = view.convertRect(view.bounds, toView:nil)
|
382
|
+
f.origin.x *= 2
|
383
|
+
f.origin.y *= 2
|
384
|
+
f.size.width *= 2
|
385
|
+
f.size.height *= 2
|
386
|
+
|
387
|
+
btn = XrayChooseViewButton.alloc.initWithFrame(view.bounds)
|
388
|
+
btn.transform = CGAffineTransformConcat(view.transform, get_transform(false))
|
389
|
+
btn.frame = f
|
390
|
+
btn.target = view
|
391
|
+
btn.on(:touch_down_repeat) {
|
392
|
+
did_choose_view(view)
|
393
|
+
}
|
394
|
+
|
395
|
+
btn.children = children
|
396
|
+
|
397
|
+
btn.setImage(view.uiimage, forState: :normal.uicontrolstate)
|
398
|
+
btn.accessibilityLabel = "choose #{view.accessibilityLabel}"
|
399
|
+
btn.layer.borderColor = :white.uicolor.cgcolor
|
400
|
+
btn.layer.borderWidth = 1
|
401
|
+
view.hide
|
402
|
+
|
403
|
+
children + [btn]
|
404
|
+
end
|
405
|
+
|
406
|
+
def select(selected)
|
407
|
+
return unless selected
|
408
|
+
|
409
|
+
@selected = selected
|
410
|
+
@top_bar.text = @selected.class.name
|
411
|
+
index = @table_source.subviews.index { |item| item[:view] == @selected }
|
412
|
+
index_path = [0, index].nsindexpath
|
413
|
+
@table.selectRowAtIndexPath(index_path, animated:true, scrollPosition:UITableViewScrollPositionNone)
|
414
|
+
@table.scrollToRowAtIndexPath(index_path, atScrollPosition: UITableViewScrollPositionNone, animated:true)
|
415
|
+
|
416
|
+
UIView.animate {
|
417
|
+
if @selected == Xray.window
|
418
|
+
selector_frame = [[0, 0], [half_screen_width, half_screen_height]]
|
419
|
+
else
|
420
|
+
selector_frame = Xray.window.convertRect(@selected.bounds, fromView:@selected)
|
421
|
+
end
|
422
|
+
@selector.frame = selector_frame
|
423
|
+
}
|
424
|
+
end
|
425
|
+
|
426
|
+
def edit(target)
|
427
|
+
collapse_picker
|
428
|
+
@bottom_bar.text = target.to_s
|
429
|
+
|
430
|
+
Xray.plugins.each do |plugin|
|
431
|
+
plugin.edit(target)
|
432
|
+
end
|
433
|
+
@target = target
|
434
|
+
SugarCube::Adjust::adjust(target)
|
435
|
+
reset
|
436
|
+
end
|
437
|
+
|
438
|
+
def reset
|
439
|
+
@toolbar.show
|
440
|
+
@canvas.contentOffset = [0, 0]
|
441
|
+
end
|
442
|
+
|
443
|
+
def toggle_picker
|
444
|
+
if @picker_is_expanded
|
445
|
+
collapse_picker
|
446
|
+
else
|
447
|
+
expand_picker
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
def expand_picker
|
452
|
+
return if @picker_is_expanded
|
453
|
+
UIView.animate {
|
454
|
+
@top_half.frame = [[0, 0], [full_screen_width, half_screen_height]]
|
455
|
+
@expand_button.transform = CGAffineTransformMakeRotation(0.degrees)
|
456
|
+
}
|
457
|
+
@picker_is_expanded = true
|
458
|
+
end
|
459
|
+
|
460
|
+
def collapse_picker
|
461
|
+
return unless @picker_is_expanded
|
462
|
+
UIView.animation_chain {
|
463
|
+
@top_half.frame = [[half_screen_width, 0], [full_screen_width, half_screen_height]]
|
464
|
+
@expand_button.transform = CGAffineTransformMakeRotation(180.degrees)
|
465
|
+
}.and_then {
|
466
|
+
@top_half.frame = [[half_screen_width, 0], [half_screen_width, half_screen_height]]
|
467
|
+
}.start
|
468
|
+
@picker_is_expanded = false
|
469
|
+
end
|
470
|
+
|
471
|
+
def tableView(table_view, didSelectRowAtIndexPath:index_path)
|
472
|
+
table_selection = @table_source.subviews[index_path.row][:view]
|
473
|
+
if @selected == table_selection
|
474
|
+
edit(table_selection)
|
475
|
+
else
|
476
|
+
select(table_selection)
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
def update_orientation(animate=true)
|
481
|
+
case UIApplication.sharedApplication.statusBarOrientation
|
482
|
+
when UIInterfaceOrientationPortrait
|
483
|
+
when UIInterfaceOrientationPortraitUpsideDown
|
484
|
+
when UIInterfaceOrientationLandscapeLeft
|
485
|
+
when UIInterfaceOrientationLandscapeRight
|
486
|
+
end
|
487
|
+
|
488
|
+
apply_transform
|
489
|
+
select(@selected)
|
490
|
+
edit(@target)
|
491
|
+
end
|
492
|
+
|
493
|
+
def get_transform(scale=true)
|
494
|
+
if scale
|
495
|
+
dx = -Xray.app_bounds.width / 4
|
496
|
+
dy = -Xray.app_bounds.height / 4
|
497
|
+
teh_transform = CGAffineTransformMakeTranslation(dx, dy)
|
498
|
+
teh_transform = CGAffineTransformScale(teh_transform, 0.5, 0.5)
|
499
|
+
else
|
500
|
+
teh_transform = CGAffineTransformIdentity
|
501
|
+
end
|
502
|
+
|
503
|
+
case UIApplication.sharedApplication.statusBarOrientation
|
504
|
+
when UIInterfaceOrientationPortraitUpsideDown
|
505
|
+
teh_transform = CGAffineTransformRotate(teh_transform, 180.degrees)
|
506
|
+
when UIInterfaceOrientationLandscapeLeft
|
507
|
+
teh_transform = CGAffineTransformRotate(teh_transform, -90.degrees)
|
508
|
+
when UIInterfaceOrientationLandscapeRight
|
509
|
+
teh_transform = CGAffineTransformRotate(teh_transform, 90.degrees)
|
510
|
+
end
|
511
|
+
|
512
|
+
return teh_transform
|
513
|
+
end
|
514
|
+
|
515
|
+
def apply_transform(animate=true)
|
516
|
+
teh_transform = get_transform
|
517
|
+
|
518
|
+
UIView.animate(duration: animate ? nil : 0) do
|
519
|
+
@revert[:views].each do |subview|
|
520
|
+
subview.transform = teh_transform
|
521
|
+
end
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
def get_screenshot
|
526
|
+
scale = UIScreen.mainScreen.scale
|
527
|
+
UIGraphicsBeginImageContextWithOptions(Xray.window.bounds.size, false, scale)
|
528
|
+
context = UIGraphicsGetCurrentContext()
|
529
|
+
|
530
|
+
@revert[:views].each do |subview|
|
531
|
+
CGContextSaveGState(context)
|
532
|
+
CGContextTranslateCTM(context, subview.frame.origin.x, subview.frame.origin.y)
|
533
|
+
|
534
|
+
subview.layer.renderInContext(context)
|
535
|
+
CGContextRestoreGState(context)
|
536
|
+
end
|
537
|
+
image = UIGraphicsGetImageFromCurrentImageContext()
|
538
|
+
UIGraphicsEndImageContext()
|
539
|
+
return image
|
540
|
+
end
|
541
|
+
|
542
|
+
end
|
543
|
+
|
544
|
+
|
545
|
+
class XrayTableSource
|
546
|
+
attr :selected
|
547
|
+
attr :subviews
|
548
|
+
|
549
|
+
def initialize(selected, subviews)
|
550
|
+
@selected = selected
|
551
|
+
@subviews = subviews
|
552
|
+
end
|
553
|
+
|
554
|
+
def [](index)
|
555
|
+
@subviews[index]
|
556
|
+
end
|
557
|
+
|
558
|
+
##|
|
559
|
+
##| TABLEVIEW
|
560
|
+
##|
|
561
|
+
def numberOfSectionsInTableView(table_view)
|
562
|
+
1
|
563
|
+
end
|
564
|
+
|
565
|
+
def tableView(table_view, numberOfRowsInSection:section)
|
566
|
+
@subviews.length
|
567
|
+
end
|
568
|
+
|
569
|
+
def tableView(table_view, cellForRowAtIndexPath:index_path)
|
570
|
+
cell_identifier = "XrayTableCell"
|
571
|
+
cell = table_view.dequeueReusableCellWithIdentifier(cell_identifier)
|
572
|
+
|
573
|
+
unless cell
|
574
|
+
cell = XrayTableCell.alloc.initWithStyle(:default.uitablecellstyle,
|
575
|
+
reuseIdentifier: cell_identifier)
|
576
|
+
end
|
577
|
+
|
578
|
+
view_info = @subviews[index_path.row]
|
579
|
+
view = view_info[:view]
|
580
|
+
cell.view = view
|
581
|
+
cell.depth = view_info[:depth]
|
582
|
+
text = ''
|
583
|
+
indent = view_info[:indent]
|
584
|
+
text << indent << view.to_s
|
585
|
+
cell.textLabel.text = text
|
586
|
+
cell.row = index_path.row
|
587
|
+
cell.detail_button.off :touch
|
588
|
+
cell.detail_button.hide
|
589
|
+
|
590
|
+
return cell
|
591
|
+
end
|
592
|
+
|
593
|
+
end
|
594
|
+
|
595
|
+
|
596
|
+
class XrayTableCell < UITableViewCell
|
597
|
+
attr_accessor :view
|
598
|
+
attr_accessor :row
|
599
|
+
attr :detail_button
|
600
|
+
attr_accessor :depth
|
601
|
+
|
602
|
+
def initWithStyle(style, reuseIdentifier:identifier)
|
603
|
+
super.tap do
|
604
|
+
textLabel.font = :monospace.uifont(10)
|
605
|
+
textLabel.lineBreakMode = :clip.uilinebreakmode
|
606
|
+
@detail_button = XrayDetailButton.alloc.init
|
607
|
+
@detail_button.frame = [[143, -0.5], [17, 19]]
|
608
|
+
contentView << @detail_button
|
609
|
+
|
610
|
+
self.detail_button.on :touch {
|
611
|
+
Xray.ui.select(self.view) if self.view
|
612
|
+
}
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
def accessibilityLabel
|
617
|
+
"depth #{depth}, instance of #{view.class}" + (view.superview ? ", child of #{view.superview.class}" : '')
|
618
|
+
end
|
619
|
+
|
620
|
+
end
|
621
|
+
|
622
|
+
class XrayDetailButton < UIButton
|
623
|
+
|
624
|
+
def init
|
625
|
+
initWithFrame([[0, 0], [27, 29]])
|
626
|
+
end
|
627
|
+
|
628
|
+
def initWithFrame(frame)
|
629
|
+
super.tap do
|
630
|
+
setImage('xray_detail_button'.uiimage, forState: :normal.uicontrolstate)
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
def pointInside(point, withEvent:event)
|
635
|
+
bounds.contains?(point)
|
636
|
+
end
|
637
|
+
|
638
|
+
end
|
639
|
+
|
640
|
+
class XrayChooseViewButton < UIButton
|
641
|
+
attr_accessor :container
|
642
|
+
attr_accessor :label
|
643
|
+
attr_accessor :target
|
644
|
+
attr_accessor :children
|
645
|
+
|
646
|
+
def initWithFrame(frame)
|
647
|
+
super.tap do
|
648
|
+
self.backgroundColor = :clear.uicolor
|
649
|
+
@@fade_out_timer = nil
|
650
|
+
@@slide_out_timer = nil
|
651
|
+
|
652
|
+
self.on(:touch_down) {
|
653
|
+
start_touch
|
654
|
+
}
|
655
|
+
self.on(:touch_stop) {
|
656
|
+
stop_touch
|
657
|
+
}
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
def slide_out_timer=(timer)
|
662
|
+
if @@slide_out_timer
|
663
|
+
@@slide_out_timer.invalidate
|
664
|
+
end
|
665
|
+
@@slide_out_timer = timer
|
666
|
+
end
|
667
|
+
|
668
|
+
def fade_out_timer=(timer)
|
669
|
+
if @@fade_out_timer
|
670
|
+
@@fade_out_timer.invalidate
|
671
|
+
end
|
672
|
+
@@fade_out_timer = timer
|
673
|
+
end
|
674
|
+
|
675
|
+
def start_touch
|
676
|
+
if @@fade_out_timer
|
677
|
+
self.fade_out_timer = nil
|
678
|
+
@container.alpha = 1
|
679
|
+
else
|
680
|
+
@container.alpha = 0
|
681
|
+
@container.fade_in
|
682
|
+
end
|
683
|
+
|
684
|
+
@label.text = target.inspect
|
685
|
+
@label.sizeToFit
|
686
|
+
@label.frame = @label.frame.width([Xray.window.frame.width - 10, @label.frame.width].min)
|
687
|
+
@container.frame = @label.bounds.grow(5)
|
688
|
+
@container.center = @container.superview.center
|
689
|
+
|
690
|
+
self.backgroundColor = :black.uicolor(0.5)
|
691
|
+
self.slide_out_timer = 1.second.later do
|
692
|
+
stop_touch
|
693
|
+
slide_out
|
694
|
+
@@slide_out_timer = nil
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
def stop_touch
|
699
|
+
self.backgroundColor = :clear.uicolor
|
700
|
+
self.slide_out_timer = nil
|
701
|
+
self.fade_out_timer = 1.second.later do
|
702
|
+
@container.fade_out
|
703
|
+
@@fade_out_timer = nil
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
def slide_out
|
708
|
+
if target.superview == Xray.ui.tiny_view
|
709
|
+
Xray.ui.did_choose_view(Xray.window)
|
710
|
+
else
|
711
|
+
self.children.each do |child|
|
712
|
+
child.slide_out
|
713
|
+
end
|
714
|
+
self.children = []
|
715
|
+
self.slide(:up, Xray.window.bounds.width) {
|
716
|
+
self.removeFromSuperview
|
717
|
+
}
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
end
|
722
|
+
|
723
|
+
end end
|