pbind 0.6.2 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
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