pbind 0.6.2 → 0.8.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9a94bcb031c5d66f5604647869500bfacd9a60f
4
- data.tar.gz: c38e22368baedb9de2f70f1a9ecd2beda916a971
3
+ metadata.gz: a5f96f2b098154b4bb59743bed848868acd9d808
4
+ data.tar.gz: 630da57494ad83f41a9e20ba9f74e487ee456c6f
5
5
  SHA512:
6
- metadata.gz: baf0c17f1abe1af7194306e00118fa8b2cbd402d1c44b51fa0201e6c922af8c538d527806e86fd5e843958dd919ddc096262444215c313c0ee585282a03ea2ef
7
- data.tar.gz: c2dedc53d31e946d05da7dc884e017842bc4c84f66c5a1be3c936a6d675e81ef8a52405b5e7e56585cf1b1a18d2359ceab8d1c478b6182a4e13c034efad0d766
6
+ metadata.gz: bb44831ec0c62561b94149992a5f69bd358a54edd076518afc34e02a5237e06990ba234cf5ef2313fa5473a80f805555fb889977110c25a2b18cb5fe54f825b8
7
+ data.tar.gz: 1bb0e99fce43907ab382814832ef081251ef2671dbe1eb9e36e0a386b267a058af15e49241f808e0bb830da60c67e2f46d3c520869d3c721a269229cb9efdfe8
@@ -1,6 +1,7 @@
1
1
  require 'colored'
2
2
  require 'claide'
3
3
 
4
+ require 'pbind/minify'
4
5
  require 'pbind/user_interface'
5
6
  require 'pbind/gem_version.rb'
6
7
 
@@ -10,6 +11,7 @@ module Pbind
10
11
  require_relative 'command/watch'
11
12
  require_relative 'command/mock'
12
13
  require_relative 'command/serv'
14
+ require_relative 'command/view'
13
15
 
14
16
  self.abstract_command = true
15
17
  self.command = 'pbind'
@@ -27,7 +27,7 @@ module Pbind
27
27
  end
28
28
 
29
29
  def run
30
- @action = @action.gsub(/\//, '-')
30
+ @action = @action.gsub(/\//, ':')
31
31
  @api_name = 'PBLocalhost'
32
32
  @project_root = File.dirname(@project_path)
33
33
  @api_install_dir = File.absolute_path(File.join(@project_root, @api_name))
@@ -27,6 +27,7 @@ module Pbind
27
27
  @project_root = File.dirname(@project_path)
28
28
  @src_install_dir = File.absolute_path(File.join(@project_root, @src_name))
29
29
  @api_install_dir = File.absolute_path(File.join(@project_root, @api_name))
30
+ @api_ignores_file = File.absolute_path(File.join(@api_install_dir, 'ignore.h'))
30
31
  @project = Xcodeproj::Project.open(@project_path)
31
32
  @changed = false
32
33
 
@@ -59,6 +60,7 @@ module Pbind
59
60
  server = TCPServer.new 8082 # Server bind to port
60
61
  @server = server
61
62
  @clients = []
63
+ @client_names = Hash.new
62
64
 
63
65
  addr = server.addr
64
66
  addr.shift
@@ -79,10 +81,24 @@ module Pbind
79
81
  end
80
82
 
81
83
  def handle_request(client, req)
82
- if req.start_with?('[C]')
83
- puts "client #{req[3..-1].green} connected"
84
- elsif req.end_with?('.json')
85
- send_json([client], req)
84
+ type = req.bytes[0]
85
+ msg = req[1..-1]
86
+
87
+ if type == 0xC0
88
+ # Connected
89
+ @client_names[client] = msg
90
+ print_client_msg(client, "Connected")
91
+ elsif type == 0xC1
92
+ # Request API
93
+ print_client_msg(client, "Request API '#{msg}'")
94
+ send_json([client], msg)
95
+ elsif type == 0xC2
96
+ # Log
97
+ print_client_msg(client, msg)
98
+ elsif type == 0xF1
99
+ print_client_msg(client, "Apply changed '#{msg}'")
100
+ elsif type == 0xD0
101
+ print_client_msg(client, "Got response '#{msg}'")
86
102
  end
87
103
  end
88
104
 
@@ -116,16 +132,38 @@ module Pbind
116
132
  @listener.start # not blocking
117
133
  end
118
134
 
119
- def send_json(clients, json_file)
135
+ def send_json(clients, api)
136
+ # Check if ignores
137
+ file = File.open(@api_ignores_file, "r")
138
+ file.each_line { |line|
139
+ if line.start_with? '//'
140
+ next
141
+ end
142
+
143
+ ignore = line.chomp
144
+ if ignore.include? api
145
+ print_serv_msg "Ignores API '#{api}'"
146
+ clients.each { |client|
147
+ write_byte client, 0xE0
148
+ }
149
+ return
150
+ end
151
+ }
152
+
153
+ # Read content and minify
154
+ json_file = "#{api}.json"
120
155
  file = File.open(File.join(@api_install_dir, json_file), "r")
121
156
  content = file.read
122
157
  file.close
123
158
 
124
- UI.section("Send API \"/#{File.basename(json_file, '.json')}\"") {
125
- clients.each { |client|
126
- write_byte client, 0xE0
127
- write_string client, content
128
- }
159
+ content = Minify.json(content)
160
+
161
+ # Send content
162
+ print_serv_msg("Send API '#{api}'")
163
+ clients.each { |client|
164
+ write_byte client, 0xD0
165
+ write_string client, api
166
+ write_string client, content
129
167
  }
130
168
  end
131
169
 
@@ -145,12 +183,12 @@ module Pbind
145
183
  if file_name == nil
146
184
  file_name = File.basename(file_path)
147
185
  end
148
- UI.section("Update file \"#{file_name}\"") {
149
- clients.each { |client|
150
- write_byte(client, 0xF1)
151
- write_string(client, file_name)
152
- write_string(client, file_content)
153
- }
186
+
187
+ print_serv_msg("Update file \"#{file_name}\"")
188
+ clients.each { |client|
189
+ write_byte(client, 0xF1)
190
+ write_string(client, file_name)
191
+ write_string(client, file_content)
154
192
  }
155
193
  }
156
194
  end
@@ -172,6 +210,30 @@ module Pbind
172
210
  end
173
211
  end
174
212
 
213
+ def print_serv_msg(msg)
214
+ print_time
215
+ print "[Pbind] ".yellow
216
+ puts msg
217
+ end
218
+
219
+ def print_client_msg(client, msg)
220
+ print_time
221
+ device = @client_names[client]
222
+ if (device == nil)
223
+ device = "unknown"
224
+ end
225
+ print "[#{device}] ".green
226
+ puts msg
227
+ end
228
+
229
+ def print_time
230
+ t = Time.now
231
+ print t.strftime("%H:%M:%S")
232
+ print '.'
233
+ print '%03d' % ((t.to_f * 1000).to_i % 1000) # ms
234
+ print ' '
235
+ end
236
+
175
237
  end
176
238
  end
177
239
  end
@@ -0,0 +1,86 @@
1
+ require 'xcodeproj'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+
5
+ module Pbind
6
+ class Command
7
+ class View < Command
8
+ self.abstract_command = false
9
+ self.summary = 'Generate the objective-c view code from Pbind layout.'
10
+ self.description = <<-DESC
11
+ This will parse the `PLIST` file and translate it to objective-c code.
12
+ DESC
13
+
14
+ self.arguments = [
15
+ CLAide::Argument.new(%(PLIST), true),
16
+ ]
17
+
18
+ def initialize(argv)
19
+ super
20
+ @plist = argv.shift_argument
21
+ end
22
+
23
+ def validate!
24
+ help! 'The plist is required.' unless @plist
25
+ help! 'The plist is not exists.' unless File.exists?(@plist)
26
+ end
27
+
28
+ def run
29
+ parse_plist(@plist)
30
+ super
31
+ end
32
+
33
+ private
34
+
35
+ #----------------------------------------#
36
+
37
+ # !@group Private helpers
38
+
39
+ # Parse the plist and generate code
40
+ #
41
+ # @return [void]
42
+ #
43
+ def parse_plist(plist_path)
44
+ plist = Xcodeproj::Plist.read_from_path(plist_path)
45
+ names = []
46
+ parents = []
47
+
48
+ puts '// Create views'
49
+ plist["views"].each { |name, view|
50
+ clazz = view['clazz']
51
+ properties = view['properties']
52
+
53
+ puts "#{clazz} *#{name} = [[#{clazz} alloc] init]; {"
54
+
55
+ properties.each { |key, value|
56
+ puts " #{name}.#{key} = [PBValueParser valueWithString:@\"#{value}\"];"
57
+ }
58
+ puts " [self addSubview:#{name}];"
59
+
60
+ puts '}'
61
+ names.push name
62
+ }
63
+
64
+ puts ''
65
+ puts '// Auto layout'
66
+ puts 'NSDictionary *views = @{'
67
+ names.each { |name|
68
+ puts " @\"#{name}\": #{name},"
69
+ }
70
+ puts '};'
71
+ puts 'NSArray *formats = @['
72
+ plist["formats"].each { |format|
73
+ puts " @\"#{format}\","
74
+ }
75
+ puts "];"
76
+ puts <<-CODE
77
+ for (NSString *format in formats) {
78
+ NSArray *constraints = [PBLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:metrics views:views];
79
+ [view addConstraints:constraints];
80
+ }
81
+ CODE
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -1,5 +1,5 @@
1
1
  module Pbind
2
2
  # The version of the Pbind command line tool.
3
3
  #
4
- VERSION = '0.6.2'.freeze unless defined? Pbind::VERSION
4
+ VERSION = '0.8.2'.freeze unless defined? Pbind::VERSION
5
5
  end
@@ -0,0 +1,52 @@
1
+ module Pbind
2
+ module Minify
3
+
4
+ require 'strscan'
5
+
6
+ class << self
7
+
8
+ def json(str)
9
+
10
+ ss = StringScanner.new(str)
11
+ buf = ''
12
+
13
+ until ss.eos?
14
+ # Remove whitespace
15
+ if s = ss.scan(/\s+/)
16
+
17
+ # Scan punctuation
18
+ elsif s = ss.scan(/[{},:\]\[]/)
19
+ buf << s
20
+
21
+ # Scan strings
22
+ elsif s = ss.scan(/""|(".*?[^\\])?"/)
23
+ buf << s
24
+
25
+ # Scan reserved words
26
+ elsif s = ss.scan(/(true|false|null)/)
27
+ buf << s
28
+
29
+ # Scan numbers
30
+ elsif s = ss.scan(/-?\d+([.]\d+)?([eE][-+]?[0-9]+)?/)
31
+ buf << s
32
+
33
+ # Remove C++ style comments
34
+ elsif s = ss.scan(%r{//})
35
+ ss.scan_until(/(\r?\n|$)/)
36
+
37
+ # Remove C style comments
38
+ elsif s = ss.scan(%r{/[*]})
39
+ ss.scan_until(%r{[*]/}) || raise(SyntaxError, "Unterminated /*...*/ comment - #{ss.rest}")
40
+
41
+ # Anything else is invalid JSON
42
+ else
43
+ raise SyntaxError, "Unable to pre-scan string: #{ss.rest}"
44
+ end
45
+ end
46
+ buf
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
@@ -21,8 +21,18 @@
21
21
 
22
22
  - (NSData *)readData {
23
23
  int length = [self readInt];
24
+ if (length < 0) {
25
+ return nil;
26
+ }
24
27
  uint8_t *bytes = malloc(length);
25
28
  NSInteger len = [self read:bytes maxLength:length];
29
+ while (len < length) {
30
+ len += [self read:bytes + len maxLength:length - len];
31
+ }
32
+ if (len != length) {
33
+ NSLog(@"Failed to read data in length %i, only got %i bytes", length, (int)len);
34
+ return nil;
35
+ }
26
36
  return [NSData dataWithBytes:bytes length:len];
27
37
  }
28
38
 
@@ -9,7 +9,7 @@
9
9
  #import "PBLLOptions.h"
10
10
  #include <targetconditionals.h>
11
11
 
12
- #if (PBLIVE_ENABLED && !(TARGET_IPHONE_SIMULATOR))
12
+ #if (PBLIVE_ENABLED)
13
13
 
14
14
  #import <UIKit/UIKit.h>
15
15
 
@@ -17,7 +17,7 @@
17
17
 
18
18
  + (instancetype)sharedInspector;
19
19
 
20
- + (void)addToWindow;
20
+ - (void)updateConnectState:(BOOL)connected;
21
21
 
22
22
  @end
23
23
 
@@ -7,44 +7,407 @@
7
7
 
8
8
  #import "PBLLInspector.h"
9
9
 
10
- #if (PBLIVE_ENABLED && !(TARGET_IPHONE_SIMULATOR))
10
+ #if (PBLIVE_ENABLED)
11
11
 
12
12
  #import "PBLLInspectorController.h"
13
+ #import "PBLLInspectorTipsController.h"
14
+ #import "PBLLResource.h"
13
15
  #import <Pbind/Pbind.h>
16
+ #import <objc/runtime.h>
14
17
 
15
- @interface PBLLInspector () <UISearchBarDelegate>
18
+ @interface PBLLTipsTapper : UITapGestureRecognizer
19
+
20
+ @property (nonatomic, strong) NSString *tips;
21
+
22
+ @property (nonatomic, weak) UIView *owner;
23
+
24
+ @property (nonatomic, copy) NSString *keyPath;
25
+
26
+ @property (nonatomic, assign) BOOL ownerUserInteractionEnabled;
27
+
28
+ @end
29
+
30
+ @implementation PBLLTipsTapper
31
+
32
+ - (NSString *)tips {
33
+ if (_tips != nil) {
34
+ return _tips;
35
+ }
36
+
37
+ NSMutableString *s = [NSMutableString string];
38
+ [s appendString:[_owner valueForAdditionKey:[NSString stringWithFormat:@"~%@", _keyPath]]];
39
+ if ([_owner isKindOfClass:[UIImageView class]]) {
40
+ UIImage *image = [(UIImageView *)_owner image];
41
+ [s appendFormat:@"\nsize: %.1f x %.1f @%.1fx", image.size.width, image.size.height, image.scale];
42
+ }
43
+ return s;
44
+ }
45
+
46
+ @end
47
+
48
+ @interface PBLLInspector () <UIPopoverPresentationControllerDelegate>
49
+ {
50
+ NSString *_identifier;
51
+ }
52
+
53
+ + (void)showInspectorForView:(UIView *)view;
54
+
55
+ @end
56
+
57
+ @implementation UIViewController (PBLLInspector)
58
+
59
+ static inline void pbll_swizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector) {
60
+ Method originalMethod = class_getInstanceMethod(class, originalSelector);
61
+ Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
62
+ if (class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
63
+ class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
64
+ } else {
65
+ method_exchangeImplementations(originalMethod, swizzledMethod);
66
+ }
67
+ }
68
+
69
+ + (void)load {
70
+ static dispatch_once_t onceToken;
71
+ dispatch_once(&onceToken, ^{
72
+ pbll_swizzleSelector([UIViewController class], @selector(viewDidAppear:), @selector(pbll_viewDidAppear:));
73
+ });
74
+ }
75
+
76
+ - (void)pbll_viewDidAppear:(BOOL)animated {
77
+ [self pbll_viewDidAppear:animated];
78
+
79
+ if ([self isKindOfClass:[UINavigationController class]] || [self isKindOfClass:[PBLLInspectorController class]]) {
80
+ return;
81
+ }
82
+
83
+ UIView *plistView = [self pbll_findPlistView:self.view];
84
+ if (plistView == nil) {
85
+ return;
86
+ }
87
+ [PBLLInspector showInspectorForView:plistView];
88
+ }
89
+
90
+ - (UIView *)pbll_findPlistView:(UIView *)view {
91
+ if (view.plist != nil) {
92
+ return view;
93
+ }
94
+
95
+ UIView *plistView = nil;
96
+ for (UIView *subview in view.subviews) {
97
+ plistView = [self pbll_findPlistView:subview];
98
+ if (plistView != nil) {
99
+ break;
100
+ }
101
+ }
102
+ return plistView;
103
+ }
16
104
 
17
105
  @end
18
106
 
19
107
  @implementation PBLLInspector
20
108
 
109
+ static const CGFloat kWidth = 44.f;
110
+ static const CGFloat kHeight = 44.f;
111
+
112
+ static BOOL kDisplaysExpression = NO;
113
+
114
+ + (void)load {
115
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pbactionWillDispatch:) name:PBActionStoreWillDispatchActionNotification object:nil];
116
+ [Pbind registerViewValueSetter:^id(UIView *view, NSString *keyPath, id value, BOOL *canceld, UIView *contextView, NSString *contextKeyPath) {
117
+ if (!kDisplaysExpression) {
118
+ return value;
119
+ }
120
+
121
+ PBExpression *exp = [[contextView pb_expressions] objectForKey:contextKeyPath];
122
+ if (exp == nil) {
123
+ return value;
124
+ }
125
+
126
+ if ([keyPath isEqualToString:@"text"]) {
127
+ return [exp stringValue];
128
+ }
129
+
130
+ return value;
131
+ }];
132
+
133
+ [Pbind registerViewValueAsyncSetter:^(UIView *view, NSString *keyPath, id value, CGSize viewSize, UIView *contextView, NSString *contextKeyPath) {
134
+ PBExpression *exp = [[contextView pb_expressions] objectForKey:contextKeyPath];
135
+ if (exp == nil) {
136
+ return;
137
+ }
138
+
139
+ if ([view isKindOfClass:[UIImageView class]] && [keyPath isEqualToString:@"image"]) {
140
+ CALayer *imageBgInspectorLayer = [view valueForAdditionKey:@"pb_bg_inspector"];
141
+ CATextLayer *textInspectorLayer = [view valueForAdditionKey:@"pb_text_inspector"];
142
+ PBLLTipsTapper *tapper = [view valueForAdditionKey:@"pb_tap_inspector"];
143
+ if (kDisplaysExpression) {
144
+ CGRect bounds = (CGRect){.origin = CGPointZero, .size = viewSize};
145
+ if (imageBgInspectorLayer == nil) {
146
+ imageBgInspectorLayer = [[CALayer alloc] init];
147
+ imageBgInspectorLayer.backgroundColor = [[PBLLResource pbindColor] colorWithAlphaComponent:.5f].CGColor;
148
+ imageBgInspectorLayer.bounds = bounds;
149
+ imageBgInspectorLayer.position = CGPointZero;
150
+ imageBgInspectorLayer.anchorPoint = CGPointZero;
151
+ [view.layer addSublayer:imageBgInspectorLayer];
152
+ [view setValue:imageBgInspectorLayer forAdditionKey:@"pb_bg_inspector"];
153
+ }
154
+
155
+ if (textInspectorLayer == nil) {
156
+ textInspectorLayer = [[CATextLayer alloc] init];
157
+ textInspectorLayer.bounds = CGRectMake(0, 0, viewSize.width, 36);
158
+ textInspectorLayer.position = CGPointMake(0, viewSize.height / 2);
159
+ textInspectorLayer.anchorPoint = CGPointMake(0, 0.5);
160
+ textInspectorLayer.foregroundColor = [UIColor whiteColor].CGColor;
161
+ textInspectorLayer.alignmentMode = kCAAlignmentCenter;
162
+ textInspectorLayer.contentsScale = [UIScreen mainScreen].scale;
163
+ textInspectorLayer.fontSize = 14.f;
164
+ [view.layer addSublayer:textInspectorLayer];
165
+ [view setValue:textInspectorLayer forAdditionKey:@"pb_text_inspector"];
166
+ }
167
+ textInspectorLayer.string = [NSString stringWithFormat:@"%@\n%.1f x %.1f", exp.stringValue, viewSize.width, viewSize.height];
168
+
169
+ if (tapper == nil) {
170
+ tapper = [[PBLLTipsTapper alloc] initWithTarget:self action:@selector(handleShowTips:)];
171
+ tapper.owner = view;
172
+ tapper.keyPath = keyPath;
173
+ [view addGestureRecognizer:tapper];
174
+ [view setValue:tapper forAdditionKey:@"pb_tap_inspector"];
175
+
176
+ tapper.ownerUserInteractionEnabled = view.userInteractionEnabled;
177
+ view.userInteractionEnabled = YES;
178
+ }
179
+ } else {
180
+ if (imageBgInspectorLayer != nil) {
181
+ [imageBgInspectorLayer removeFromSuperlayer];
182
+ [view setValue:nil forAdditionKey:@"pb_bg_inspector"];
183
+ }
184
+ if (textInspectorLayer != nil) {
185
+ [textInspectorLayer removeFromSuperlayer];
186
+ [view setValue:nil forAdditionKey:@"pb_text_inspector"];
187
+ }
188
+ if (tapper != nil) {
189
+ view.userInteractionEnabled = tapper.ownerUserInteractionEnabled;
190
+ [view removeGestureRecognizer:tapper];
191
+ [view setValue:nil forAdditionKey:@"pb_tap_inspector"];
192
+ }
193
+ }
194
+ }
195
+
196
+ return;
197
+ }];
198
+ }
199
+
21
200
  + (instancetype)sharedInspector {
22
- static PBLLInspector *o;
201
+ static PBLLInspector *inspector;
23
202
  static dispatch_once_t onceToken;
24
203
  dispatch_once(&onceToken, ^{
25
- o = [PBLLInspector buttonWithType:UIButtonTypeCustom];
204
+ inspector = [[PBLLInspector alloc] initWithFrame:CGRectMake(0, 0, kWidth, kHeight)];
26
205
  });
27
- return o;
206
+ return inspector;
28
207
  }
29
208
 
30
- + (void)addToWindow {
209
+ + (void)addToController:(UIViewController *)controller withIdentifier:(NSString *)identifier {
210
+ if (controller == nil) return;
211
+
31
212
  PBLLInspector *inspector = [self sharedInspector];
32
- CGRect frame = [UIScreen mainScreen].bounds;
33
- frame.origin.x = frame.size.width - 68.f;
34
- frame.origin.y = frame.size.height - 100.f;
35
- frame.size.width = 60.f;
36
- frame.size.height = 44.f;
37
- inspector.frame = frame;
38
- inspector.backgroundColor = [UIColor greenColor];
39
- [inspector setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
40
- [inspector setTitle:@"Pbind" forState:UIControlStateNormal];
41
- [inspector addTarget:inspector action:@selector(didClick:) forControlEvents:UIControlEventTouchUpInside];
42
- [[[UIApplication sharedApplication].delegate window] addSubview:inspector];
43
- }
44
-
45
- - (void)didClick:(id)sender {
46
- PBLLInspectorController *controller = [[PBLLInspectorController alloc] init];
47
- [PBTopController().navigationController pushViewController:controller animated:YES];
213
+ if ([inspector.superview isEqual:controller.view]) {
214
+ return;
215
+ }
216
+
217
+ [inspector removeFromSuperview];
218
+ [controller.view addSubview:inspector];
219
+
220
+ CGPoint center = CGPointZero;
221
+ CGFloat x = [[NSUserDefaults standardUserDefaults] floatForKey:[NSString stringWithFormat:@"pbind.inspector.x@%@", identifier]];
222
+ CGFloat y = [[NSUserDefaults standardUserDefaults] floatForKey:[NSString stringWithFormat:@"pbind.inspector.y@%@", identifier]];
223
+ CGFloat minX = kWidth / 2;
224
+ CGFloat minY = kHeight / 2;
225
+ CGFloat maxX = controller.view.frame.size.width - minX;
226
+ CGFloat maxY = controller.view.frame.size.height - minY;
227
+ if (x >= minX && x <= maxX) {
228
+ center.x = x;
229
+ } else {
230
+ center.x = maxX - 6.f;
231
+ }
232
+ if (y >= minY && y <= maxY) {
233
+ center.y = y;
234
+ } else {
235
+ center.y = maxY - 70.f;
236
+ }
237
+ inspector.center = center;
238
+ inspector->_identifier = identifier;
239
+ }
240
+
241
+ - (instancetype)initWithFrame:(CGRect)frame {
242
+ if (self = [super initWithFrame:frame]) {
243
+ self.backgroundColor = [UIColor lightGrayColor];
244
+ self.layer.cornerRadius = 5.f;
245
+ [self setImage:[PBLLResource logoImage] forState:UIControlStateNormal];
246
+ // [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
247
+ // [self setTitle:@"Pbind" forState:UIControlStateNormal];
248
+ [self addTarget:self action:@selector(handleClick:) forControlEvents:UIControlEventTouchUpInside];
249
+
250
+ UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
251
+ [self addGestureRecognizer:pan];
252
+
253
+ #if !(TARGET_IPHONE_SIMULATOR)
254
+ UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
255
+ [self addGestureRecognizer:longPress];
256
+ #endif
257
+ }
258
+ return self;
259
+ }
260
+
261
+ - (void)willMoveToWindow:(UIWindow *)newWindow {
262
+ if (newWindow == nil) {
263
+ UIViewController *vc = [self supercontroller];
264
+ if (vc.navigationController.topViewController != vc) {
265
+ // pop
266
+ kDisplaysExpression = NO;
267
+ }
268
+ }
269
+ }
270
+
271
+ #pragma mark - User Interaction
272
+
273
+ - (void)handleClick:(id)sender {
274
+ kDisplaysExpression = !kDisplaysExpression;
275
+ if (!kDisplaysExpression) {
276
+ UIViewController *curVC = [self supercontroller];
277
+ if (curVC.presentedViewController != nil) {
278
+ [curVC.presentedViewController dismissViewControllerAnimated:YES completion:nil];
279
+ }
280
+ }
281
+
282
+ [[self superview] pb_reloadPlist];
283
+ }
284
+
285
+ - (void)handlePan:(UIPanGestureRecognizer *)recognizer {
286
+ CGPoint translation = [recognizer translationInView:self];
287
+ CGPoint center = self.center;
288
+ center.x += translation.x;
289
+ center.y += translation.y;
290
+ self.center = center;
291
+ [recognizer setTranslation:CGPointZero inView:self];
292
+
293
+ [[NSUserDefaults standardUserDefaults] setFloat:center.x forKey:[NSString stringWithFormat:@"pbind.inspector.x@%@", _identifier]];
294
+ [[NSUserDefaults standardUserDefaults] setFloat:center.y forKey:[NSString stringWithFormat:@"pbind.inspector.y@%@", _identifier]];
295
+ }
296
+
297
+ #if !(TARGET_IPHONE_SIMULATOR)
298
+ - (void)handleLongPress:(UILongPressGestureRecognizer *)sender {
299
+ if (sender.state != UIGestureRecognizerStateBegan) {
300
+ return;
301
+ }
302
+
303
+ UIViewController *currentController = [self supercontroller];
304
+ if (currentController == nil) {
305
+ return;
306
+ }
307
+
308
+ [PBLLInspectorController presentFromViewController:currentController];
309
+ }
310
+ #endif
311
+
312
+ + (void)handleShowTips:(PBLLTipsTapper *)sender {
313
+ if (sender.tips == nil) {
314
+ return;
315
+ }
316
+
317
+ [[self sharedInspector] showTips:sender.tips code:nil onView:sender.owner];
318
+ }
319
+
320
+ #pragma mark - Notification
321
+
322
+ - (void)updateConnectState:(BOOL)connected {
323
+ self.backgroundColor = connected ? [PBLLResource pbindColor] : [UIColor lightGrayColor];
324
+ }
325
+
326
+ + (void)pbviewDidStartLoad:(NSNotification *)notification {
327
+ UIView *view = notification.object;
328
+ [self showInspectorForView:view];
329
+ }
330
+
331
+ + (void)showInspectorForView:(UIView *)view {
332
+ NSString *plist = view.plist;
333
+ if (plist == nil) {
334
+ return;
335
+ }
336
+
337
+ UIViewController *controller = [view supercontroller];
338
+ [self addToController:controller withIdentifier:plist];
339
+ }
340
+
341
+ + (void)pbactionWillDispatch:(NSNotification *)notification {
342
+ PBAction *action = notification.object;
343
+ if (action.mapper == nil) {
344
+ return;
345
+ }
346
+
347
+ if (!kDisplaysExpression) {
348
+ return;
349
+ }
350
+
351
+ // Prevent default action
352
+ action.disabled = YES;
353
+
354
+ // Build up tips
355
+ NSMutableDictionary *temp = [NSMutableDictionary dictionary];
356
+ PBActionState *state = action.store.state;
357
+ [action.mapper initPropertiesForTarget:temp];
358
+ [action.mapper mapPropertiesToTarget:temp withData:state.data owner:state.sender context:state.context];
359
+ NSString *tips = [temp description];
360
+
361
+ // Build up code (expression)
362
+ NSString *code = [[action.mapper targetSource] description];
363
+
364
+ [[self sharedInspector] showTips:tips code:code onView:state.sender];
365
+ }
366
+
367
+ #pragma mark - Tips
368
+
369
+ - (void)showTips:(NSString *)tips code:(NSString *)code onView:(UIView *)view {
370
+ UIViewController *curVC = [self supercontroller];
371
+ if (curVC == nil) {
372
+ return;
373
+ }
374
+
375
+ PBLLInspectorTipsController *popVC = (id) curVC.presentedViewController;
376
+ UIPopoverPresentationController *popContainerVC = popVC.popoverPresentationController;
377
+ if (popContainerVC != nil) {
378
+ BOOL changed = popContainerVC.sourceView != view || ![tips isEqualToString:popVC.tips];
379
+ if (changed) {
380
+ [popVC dismissViewControllerAnimated:NO completion:^{
381
+ [self showTips:tips code:code onView:view];
382
+ }];
383
+ } else {
384
+ [popVC dismissViewControllerAnimated:YES completion:nil];
385
+ }
386
+ } else {
387
+ popVC = [[PBLLInspectorTipsController alloc] init];
388
+ popVC.modalPresentationStyle = UIModalPresentationPopover;
389
+
390
+ popContainerVC = popVC.popoverPresentationController;
391
+ popContainerVC.permittedArrowDirections = UIPopoverArrowDirectionAny;
392
+ popContainerVC.delegate = self;
393
+ popContainerVC.backgroundColor = [PBLLResource pbindColor];
394
+ popContainerVC.passthroughViews = @[curVC.view];
395
+
396
+ popVC.tips = tips;
397
+ popVC.code = code;
398
+ popContainerVC.sourceView = view;
399
+ popContainerVC.sourceRect = view.bounds;
400
+
401
+ [curVC presentViewController:popVC animated:YES completion:nil];
402
+ }
403
+ }
404
+
405
+ - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller{
406
+ return UIModalPresentationNone;
407
+ }
408
+
409
+ - (BOOL)popoverPresentationControllerShouldDismissPopover:(UIPopoverPresentationController *)popoverPresentationController{
410
+ return YES;
48
411
  }
49
412
 
50
413
  @end