motion-xray 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|