example/ivy/ios: enable demo functionality And bring iOS app up to Android capabilities. Fixes golang/go#48694 Change-Id: I0e853cab102e34053ecdb17edb99644aa2f8651a Reviewed-on: https://go-review.googlesource.com/c/mobile/+/357977 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com> Trust: Hyang-Ah Hana Kim <hyangah@gmail.com> Trust: Peter Weinberger <pjw@google.com>
diff --git a/example/ivy/ios/README.md b/example/ivy/ios/README.md index 453ccbc..bfbaa19 100644 --- a/example/ivy/ios/README.md +++ b/example/ivy/ios/README.md
@@ -17,7 +17,7 @@ go mod init work go get -d golang.org/x/mobile/bind@latest go get -d robpike.io/ivy/mobile -gomobile bind -target=ios,iossimulator,maccatalyst,macos robpike.io/ivy/mobile +gomobile bind -target=ios,iossimulator,maccatalyst,macos robpike.io/ivy/mobile robpike.io/ivy/demo ``` Place the Mobile.xcframework directory in this directory, and
diff --git a/example/ivy/ios/ivy.xcodeproj/project.pbxproj b/example/ivy/ios/ivy.xcodeproj/project.pbxproj index aeb1d30..f183085 100644 --- a/example/ivy/ios/ivy.xcodeproj/project.pbxproj +++ b/example/ivy/ios/ivy.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ @@ -316,12 +316,16 @@ ); INFOPLIST_FILE = ivy/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); ONLY_ACTIVE_ARCH = NO; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = ivy; STRIP_STYLE = debugging; SUPPORTS_MACCATALYST = YES; @@ -342,13 +346,16 @@ ); INFOPLIST_FILE = ivy/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); ONLY_ACTIVE_ARCH = NO; - PRODUCT_BUNDLE_IDENTIFIER = "com.google.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = ivy; STRIP_STYLE = debugging; SUPPORTS_MACCATALYST = YES;
diff --git a/example/ivy/ios/ivy/Base.lproj/Main.storyboard b/example/ivy/ios/ivy/Base.lproj/Main.storyboard index db250c1..18a65a9 100644 --- a/example/ivy/ios/ivy/Base.lproj/Main.storyboard +++ b/example/ivy/ios/ivy/Base.lproj/Main.storyboard
@@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19158" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="mTw-C8-NzX"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="mTw-C8-NzX"> <device id="retina6_1" orientation="portrait" appearance="light"/> <accessibilityOverrides isEnabled="YES"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19141"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <scenes> @@ -55,15 +55,38 @@ <rect key="frame" x="0.0" y="0.0" width="414" height="896"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> - <textField opaque="NO" clipsSubviews="YES" tag="1" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Type an expression" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="9SS-TP-C7c" userLabel="InputField"> - <rect key="frame" x="20" y="817" width="374" height="34"/> - <color key="backgroundColor" red="0.98823529409999999" green="0.98039215690000003" blue="0.81176470590000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <rect key="contentStretch" x="1" y="1" width="1" height="1"/> - <fontDescription key="fontDescription" name="Menlo-Bold" family="Menlo" pointSize="14"/> - <textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet" keyboardAppearance="alert" enablesReturnKeyAutomatically="YES"/> - </textField> + <stackView opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Cn1-rU-W3R"> + <rect key="frame" x="20" y="831" width="374" height="31"/> + <subviews> + <textField opaque="NO" clipsSubviews="YES" tag="1" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Type an expression" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="9SS-TP-C7c" userLabel="InputField"> + <rect key="frame" x="0.0" y="0.0" width="301.5" height="31"/> + <color key="backgroundColor" red="0.98823529409999999" green="0.98039215690000003" blue="0.81176470590000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <rect key="contentStretch" x="1" y="1" width="1" height="1"/> + <fontDescription key="fontDescription" name="Menlo-Bold" family="Menlo" pointSize="14"/> + <textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet" keyboardAppearance="alert" enablesReturnKeyAutomatically="YES" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no"/> + </textField> + <button opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eO6-bM-CwW"> + <rect key="frame" x="301.5" y="0.0" width="72.5" height="31"/> + <accessibility key="accessibilityConfiguration" label="OK"/> + <constraints> + <constraint firstAttribute="height" constant="31" id="1nb-CV-bfg"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <state key="normal" title="Button" image="return" catalog="system"/> + <connections> + <action selector="okPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="258-zN-fwW"/> + </connections> + </button> + </subviews> + <constraints> + <constraint firstItem="9SS-TP-C7c" firstAttribute="trailing" secondItem="eO6-bM-CwW" secondAttribute="leading" id="9Fe-Sf-U5p"/> + <constraint firstItem="eO6-bM-CwW" firstAttribute="top" secondItem="9SS-TP-C7c" secondAttribute="top" id="9hv-aL-4z2"/> + <constraint firstItem="eO6-bM-CwW" firstAttribute="trailing" secondItem="Cn1-rU-W3R" secondAttribute="trailing" id="A4G-YY-4Gn"/> + <constraint firstItem="9SS-TP-C7c" firstAttribute="leading" secondItem="Cn1-rU-W3R" secondAttribute="leading" id="i2m-BU-Q4a"/> + </constraints> + </stackView> <wkWebView opaque="NO" tag="2" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" layoutMarginsFollowReadableWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ga1-Py-9re"> - <rect key="frame" x="20" y="88" width="374" height="721"/> + <rect key="frame" x="20" y="88" width="374" height="737"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/> <wkWebViewConfiguration key="configuration"> <audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/> @@ -73,29 +96,44 @@ </subviews> <color key="backgroundColor" red="0.9882352941176471" green="0.98039215686274506" blue="0.81176470588235294" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <constraints> - <constraint firstItem="ga1-Py-9re" firstAttribute="trailing" secondItem="9SS-TP-C7c" secondAttribute="trailing" id="3b6-rZ-W4R"/> - <constraint firstItem="9SS-TP-C7c" firstAttribute="top" secondItem="ga1-Py-9re" secondAttribute="bottom" constant="8" id="B3O-hi-PRD"/> + <constraint firstItem="Cn1-rU-W3R" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="7DU-Hc-uk7"/> + <constraint firstItem="Cn1-rU-W3R" firstAttribute="bottom" secondItem="wfy-db-euE" secondAttribute="top" id="8mr-vY-R1v"/> + <constraint firstItem="ga1-Py-9re" firstAttribute="bottom" secondItem="Cn1-rU-W3R" secondAttribute="top" constant="-6" id="BX0-OH-IJ4"/> <constraint firstItem="ga1-Py-9re" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="Fxh-TE-BtG"/> - <constraint firstItem="ga1-Py-9re" firstAttribute="leading" secondItem="9SS-TP-C7c" secondAttribute="leading" id="MhN-JS-MpC"/> - <constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="9SS-TP-C7c" secondAttribute="bottom" constant="11" id="Yvi-br-VHk"/> + <constraint firstItem="Cn1-rU-W3R" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="LPE-LO-nct"/> <constraint firstItem="ga1-Py-9re" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="topMargin" id="p9M-sp-ZwC"/> <constraint firstItem="ga1-Py-9re" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="pOi-3s-hrr"/> </constraints> </view> <navigationItem key="navigationItem" title="Ivy" id="9fD-A5-WTg"> - <barButtonItem key="rightBarButtonItem" title="Help" id="aDa-GJ-VpX"> + <barButtonItem key="leftBarButtonItem" title="Clear" id="70x-xp-aiX"> <connections> - <segue destination="rfr-rm-AXI" kind="show" id="kuc-xA-dyM"/> + <action selector="clear:" destination="BYZ-38-t0r" id="n7k-fg-dil"/> </connections> </barButtonItem> + <rightBarButtonItems> + <barButtonItem title="Help" id="aDa-GJ-VpX"> + <connections> + <segue destination="rfr-rm-AXI" kind="show" id="kuc-xA-dyM"/> + </connections> + </barButtonItem> + <barButtonItem title="Demo" id="ZN2-PQ-aPW"> + <connections> + <action selector="demo:" destination="BYZ-38-t0r" id="NpG-yh-wMO"/> + </connections> + </barButtonItem> + </rightBarButtonItems> </navigationItem> <connections> - <outlet property="bottomConstraint" destination="Yvi-br-VHk" id="EKm-WV-Y1w"/> + <outlet property="bottomConstraint" destination="8mr-vY-R1v" id="BqX-Do-vtq"/> + <outlet property="input" destination="9SS-TP-C7c" id="jUA-25-4WI"/> + <outlet property="okButton" destination="eO6-bM-CwW" id="o9U-hD-NDr"/> + <outlet property="tape" destination="ga1-Py-9re" id="00M-9S-MAl"/> </connections> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> </objects> - <point key="canvasLocation" x="990.40178571428567" y="382.60869565217394"/> + <point key="canvasLocation" x="989.85507246376824" y="382.36607142857139"/> </scene> <!--Navigation Controller--> <scene sceneID="ZgV-45-Pf8"> @@ -130,4 +168,7 @@ <point key="canvasLocation" x="271" y="386"/> </scene> </scenes> + <resources> + <image name="return" catalog="system" width="128" height="101"/> + </resources> </document>
diff --git a/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/Contents.json b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/Contents.json index 466150b..b6cf353 100644 --- a/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -151,6 +151,13 @@ "size" : "29x29" }, { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "33x33", + "subtype" : "45mm" + }, + { "filename" : "icon-80.png", "idiom" : "watch", "role" : "appLauncher", @@ -167,6 +174,13 @@ "subtype" : "40mm" }, { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "46x46", + "subtype" : "41mm" + }, + { "filename" : "icon-100.png", "idiom" : "watch", "role" : "appLauncher", @@ -175,6 +189,13 @@ "subtype" : "44mm" }, { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "51x51", + "subtype" : "45mm" + }, + { "filename" : "icon-172.png", "idiom" : "watch", "role" : "quickLook", @@ -199,6 +220,13 @@ "subtype" : "44mm" }, { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "117x117", + "subtype" : "45mm" + }, + { "filename" : "icon-1024.png", "idiom" : "watch-marketing", "scale" : "1x",
diff --git a/example/ivy/ios/ivy/Info.plist b/example/ivy/ios/ivy/Info.plist index aad4905..9438af6 100644 --- a/example/ivy/ios/ivy/Info.plist +++ b/example/ivy/ios/ivy/Info.plist
@@ -11,7 +11,7 @@ <key>CFBundleExecutable</key> <string>$(EXECUTABLE_NAME)</string> <key>CFBundleIdentifier</key> - <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <string>com.google.$(PRODUCT_NAME:rfc1034identifier)</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key>
diff --git a/example/ivy/ios/ivy/IvyController.h b/example/ivy/ios/ivy/IvyController.h index 0d78d9c..e76bf2f 100644 --- a/example/ivy/ios/ivy/IvyController.h +++ b/example/ivy/ios/ivy/IvyController.h
@@ -14,8 +14,13 @@ @property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint; // A text input field coupled to an output "tape", rendered with a WKWebView. -@property (strong, nonatomic) UITextField *input; +@property (weak, nonatomic) IBOutlet UITextField *input; @property (strong, nonatomic) Suggestion *suggestionView; -@property (strong, nonatomic) WKWebView *tape; +@property (weak, nonatomic) IBOutlet WKWebView *tape; +@property (weak, nonatomic) IBOutlet UIButton *okButton; + +- (IBAction)clear:(id)sender; +- (IBAction)demo:(id)sender; +- (IBAction)okPressed:(id)sender; @end
diff --git a/example/ivy/ios/ivy/IvyController.m b/example/ivy/ios/ivy/IvyController.m index 42e4dd7..ecac33f 100644 --- a/example/ivy/ios/ivy/IvyController.m +++ b/example/ivy/ios/ivy/IvyController.m
@@ -9,50 +9,46 @@ @end -@implementation IvyController +@implementation IvyController { + NSArray *demo_lines; + int demo_index; +} - (void)viewDidLoad { [super viewDidLoad]; - - self.input = (UITextField *)[self.view viewWithTag:1]; + self.input.delegate = self; self.input.autocorrectionType = UITextAutocorrectionTypeNo; self.input.keyboardType = UIKeyboardTypeNumbersAndPunctuation; - + self.suggestionView = [[Suggestion alloc] init]; self.suggestionView.delegate = self; - - self.tape = [self.view viewWithTag:2]; + self.tape.UIDelegate = self; - + self->demo_lines=NULL; + + [self.okButton setTitle:@"" forState:UIControlStateNormal]; + [self.okButton setHidden:TRUE]; + [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(textDidChange:) - name:UITextFieldTextDidChangeNotification - object:self.input]; + addObserver:self + selector:@selector(textDidChange:) + name:UITextFieldTextDidChangeNotification + object:self.input]; [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(keyboardWillShow:) - name:UIKeyboardWillShowNotification - object:nil]; + addObserver:self + selector:@selector(keyboardWillShow:) + name:UIKeyboardWillShowNotification + object:nil]; [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(keyboardWillHide:) - name:UIKeyboardWillHideNotification - object:nil]; - - NSURL *bundleURL = - [[NSBundle mainBundle] URLForResource:@"tape" withExtension:@"html"]; - NSURLRequest *request = [NSURLRequest requestWithURL:bundleURL]; - [self.tape loadRequest:request]; - self.tape.UIDelegate = self; + addObserver:self + selector:@selector(keyboardWillHide:) + name:UIKeyboardWillHideNotification + object:nil]; + [self.input becomeFirstResponder]; -} - -- (void)viewDidAppear:(BOOL)animated -{ - [self.view endEditing:YES]; + [self clear:NULL]; } - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField @@ -75,33 +71,14 @@ } - (BOOL)textField:(UITextField *)textField - shouldChangeCharactersInRange:(NSRange)range - replacementString:(NSString *)str +shouldChangeCharactersInRange:(NSRange)range +replacementString:(NSString *)str { if ([str isEqualToString:@"\n"]) { - [self - appendTape:[NSString stringWithFormat:@"<b>%@</b>", [self.input text]]]; - NSString *expr = [self.input.text stringByAppendingString:@"\n"]; - NSError *err; - NSString *result = MobileEval(expr, &err); - if (err != nil) { - result = err.description; - } - result = [result - stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; - result = - [result stringByReplacingOccurrencesOfString:@"<" withString:@"<"]; - result = - [result stringByReplacingOccurrencesOfString:@">" withString:@">"]; - NSMutableArray *lines = - (NSMutableArray *)[result componentsSeparatedByString:@"\n"]; - for (NSMutableString *line in lines) { - [self appendTape:line]; - } - self.input.text = @""; + [self enterPressed]; return NO; } - + return YES; } @@ -121,51 +98,98 @@ // Move the input text field up, as the keyboard has taken some of the screen. NSDictionary *info = [aNotification userInfo]; CGRect kbFrame = - [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; + [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; NSNumber *duration = - [info objectForKey:UIKeyboardAnimationDurationUserInfoKey]; - + [info objectForKey:UIKeyboardAnimationDurationUserInfoKey]; + UIViewAnimationCurve keyboardTransitionAnimationCurve; [[info valueForKey:UIKeyboardAnimationCurveUserInfoKey] - getValue:&keyboardTransitionAnimationCurve]; + getValue:&keyboardTransitionAnimationCurve]; UIViewAnimationOptions options = - keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16; - + keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16; + [UIView animateWithDuration:duration.floatValue - delay:0 - options:options - animations:^{ - self.bottomConstraint.constant = kbFrame.size.height; + delay:0 + options:options + animations:^{ + self.bottomConstraint.constant = 0 - kbFrame.size.height; [self.view layoutIfNeeded]; - } - completion:^(BOOL finished) { + } + completion:^(BOOL finished) { [self scrollTapeToBottom]; - }]; + }]; } - (void)keyboardWillHide:(NSNotification *)aNotification { // Move the input text field back down. NSDictionary *info = [aNotification userInfo]; + NSNumber *duration = - [info objectForKey:UIKeyboardAnimationDurationUserInfoKey]; + [info objectForKey:UIKeyboardAnimationDurationUserInfoKey]; UIViewAnimationCurve keyboardTransitionAnimationCurve; [[info valueForKey:UIKeyboardAnimationCurveUserInfoKey] - getValue:&keyboardTransitionAnimationCurve]; + getValue:&keyboardTransitionAnimationCurve]; UIViewAnimationOptions options = - keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16; - + keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16; + + int offset = self.input.inputAccessoryView != NULL ? self.suggestionView.frame.size.height : 0; + [UIView animateWithDuration:duration.floatValue - delay:0 - options:options - animations:^{ - self.bottomConstraint.constant = 32; + delay:0 + options:options + animations:^{ + self.bottomConstraint.constant = 0 - offset; [self.view layoutIfNeeded]; - } - completion:^(BOOL finished) { + } + completion:^(BOOL finished) { [self scrollTapeToBottom]; - }]; + }]; +} + +- (void)enterPressed +{ + NSString *text = self.input.text; + if ([text isEqual:@""]){ + if(self->demo_lines==NULL){ + return; + } + while (demo_index < self->demo_lines.count) { + NSString *line = self->demo_lines[self->demo_index++]; + if([line hasPrefix:@"#"]) { + [self appendTape:line tag:@"comment"]; + } else { + self.input.text = line; + break; + } + } + } else if (self->demo_lines!=NULL && [text isEqual:@"quit"]) { + [self unloadDemo]; + } else { + [self appendTape:text tag:@"expr"]; + NSString *expr = [text stringByAppendingString:@"\n"]; + NSError *err; + NSString *result = MobileEval(expr, &err); + if (err != nil) { + result = err.description; + } + result = [result + stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + result = + [result stringByReplacingOccurrencesOfString:@"<" withString:@"<"]; + result = + [result stringByReplacingOccurrencesOfString:@">" withString:@">"]; + NSMutableArray *lines = + (NSMutableArray *)[result componentsSeparatedByString:@"\n"]; + for (NSMutableString *line in lines) { + if ([line hasPrefix:@"#"]) + [self appendTape:line tag:@"comment"]; + else + [self appendTape:line tag:@"result"]; + } + self.input.text = @""; + } } - (void)scrollTapeToBottom @@ -174,11 +198,46 @@ [self.tape evaluateJavaScript:scroll completionHandler:nil]; } -- (void)appendTape:(NSString *)text +- (void)appendTape:(NSString *)text tag:(NSString *)tag { - NSString *injectSrc = @"appendDiv('%@');"; - NSString *runToInject = [NSString stringWithFormat:injectSrc, text]; + NSString *injectSrc = @"appendDiv('%@','%@');"; + NSString *runToInject = [NSString stringWithFormat:injectSrc, text, tag]; [self.tape evaluateJavaScript:runToInject completionHandler:nil]; + [self scrollTapeToBottom]; } +- (void)loadDemo +{ + [self.okButton setHidden:FALSE]; + NSString *text = DemoText(); + + self->demo_lines = [text componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + self->demo_index = 0; + self.input.text = @""; + [self enterPressed]; +} +- (void)unloadDemo +{ + [self.okButton setHidden:TRUE]; + self->demo_lines=NULL; + self.input.text = @""; +} +- (IBAction)okPressed:(id)sender { + [self enterPressed]; +} + +- (IBAction)demo:(id)sender { + if (self->demo_lines) { // demo already running + [self enterPressed]; + } else { + [self loadDemo]; + } +} + +- (IBAction)clear:(id)sender { + [self unloadDemo]; + NSString *string = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] + pathForResource:@"tape" ofType:@"html"] encoding:NSUTF8StringEncoding error:NULL]; + [self.tape loadHTMLString:string baseURL:NULL]; +} @end
diff --git a/example/ivy/ios/ivy/tape.html b/example/ivy/ios/ivy/tape.html index 4b9c796..7340e90 100644 --- a/example/ivy/ios/ivy/tape.html +++ b/example/ivy/ios/ivy/tape.html
@@ -5,40 +5,56 @@ --> <html> <head> -<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no'> -<style> -body { - padding: 0; - margin: 0; - margin-top: 20px; - font-family: Menlo, monospace; + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" charset="UTF-8"> + <style> + body { + font-family: sans-serif; + } + div { + padding: 1; + white-space: pre-wrap; + font-family: "Roboto Mono", monospace; + } + .comment { + color: grey; + } + .expr { + font-weight: bold; + } + .flow-hide { + text-overflow: ellipsis; + word-break: break-all; + white-space: nowrap; + overflow: hidden; + } + .flow-show { + word-break: break-all; + } + </style> + + <script> + +function flowClick(el) { + el.classList.toggle("flow-hide"); + el.classList.toggle("flow-show"); } -.flowhide { - text-overflow: ellipsis; - word-break: break-word; - overflow-wrap: break-word; +function appendDiv(txt, tag) { + var el = document.createElement("div"); + el.innerHTML = txt; + if (tag == "comment") { + el.classList.add("comment"); + } else if (tag == "expr") { + el.classList.add("expr"); + } else { + el.classList.add("flow-show"); + el.onclick = function() { + flowClick(el); + }; + } + document.body.appendChild(el); } -.flowshow { - word-break: break-all; -} -</style> -<script> -function flowclick(el) { - el.classList.toggle("flowhide"); - el.classList.toggle("flowshow"); -} -function appendDiv(text) { - var el = document.createElement("div"); - el.classList.add("flowhide"); - el.innerHTML = text; - el.onclick = function() { - flowclick(el); - }; - document.body.appendChild(el); - window.scrollBy(0, document.body.offsetHeight); -} </script> <body> </body>