From 6067600e816aa28e258bdf58f56cd49aff300d6e Mon Sep 17 00:00:00 2001 From: Kim Wittenburg Date: Mon, 24 Nov 2014 22:43:29 +0100 Subject: [PATCH] Redesigned Error Display --- MathPad/Base.lproj/MPDocument.xib | 19 ++--- MathPad/MPDocument.m | 16 +++-- MathPad/MPExpressionView.h | 5 +- MathPad/MPExpressionView.m | 115 +++++++++++++++++++++++++----- 4 files changed, 120 insertions(+), 35 deletions(-) diff --git a/MathPad/Base.lproj/MPDocument.xib b/MathPad/Base.lproj/MPDocument.xib index b4b2d0b..68fa2bf 100644 --- a/MathPad/Base.lproj/MPDocument.xib +++ b/MathPad/Base.lproj/MPDocument.xib @@ -1,7 +1,8 @@ - + - + + @@ -35,22 +36,22 @@ - - + + - - - - + + + + - + diff --git a/MathPad/MPDocument.m b/MathPad/MPDocument.m index cc3fcd4..03d4d83 100644 --- a/MathPad/MPDocument.m +++ b/MathPad/MPDocument.m @@ -8,6 +8,8 @@ #import "MPDocument.h" +#import "MPParsedExpression.h" + @implementation MPDocument - (id)init @@ -60,13 +62,15 @@ #pragma mark Actions - (IBAction)evaluateExpression:(id)sender { - NSError *error; - NSDecimalNumber *result = [self.expressionView.expressionStorage evaluateWithError:&error]; - if (error) { - self.expressionView.errorMessageTextField.stringValue = error.localizedDescription; - } else { - self.expressionView.errorMessageTextField.stringValue = @""; + NSArray *errors; + MPParsedExpression *parsedExpression = [self.expressionView.expressionStorage parse:&errors]; + NSError *mathError; + NSDecimalNumber *result; + if (parsedExpression) { + result = [parsedExpression evaluate:&mathError]; } + self.expressionView.syntaxErrors = errors; + self.expressionView.mathError = mathError; self.resultLabel.stringValue = result != nil ? [result descriptionWithLocale:[NSLocale currentLocale]] : @""; } diff --git a/MathPad/MPExpressionView.h b/MathPad/MPExpressionView.h index 06c6fa9..65d41e2 100644 --- a/MathPad/MPExpressionView.h +++ b/MathPad/MPExpressionView.h @@ -6,7 +6,7 @@ // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // -// TODO: Undo/Redo + Delegate +// TODO: Undo/Redo + Delegate (evaluateExpressionView:evaluate...) @class MPExpressionView, MPExpressionStorage, MPExpressionLayout, MPRangePath; @@ -22,7 +22,8 @@ @property (nonatomic, strong) MPRangePath *selection; -@property (nonatomic, strong) NSTextField *errorMessageTextField; +@property (nonatomic, strong) NSError *mathError; +@property (nonatomic, strong) NSArray *syntaxErrors; @property (nonatomic, weak) id target; @property (nonatomic) SEL action; diff --git a/MathPad/MPExpressionView.m b/MathPad/MPExpressionView.m index 5edcd89..ac72ca7 100644 --- a/MathPad/MPExpressionView.m +++ b/MathPad/MPExpressionView.m @@ -10,6 +10,7 @@ #import "MPExpression.h" #import "MPExpressionElement.h" +#import "MPParsedExpression.h" #import "MPPowerFunction.h" #import "MPParenthesisFunction.h" @@ -20,6 +21,7 @@ #import "MPExpressionStorage.h" #import "MPExpressionLayout.h" +#import "MPFunctionLayout.h" #import "MPFunctionsViewController.h" @@ -33,6 +35,9 @@ @property (nonatomic, strong) NSPopover *functionsPopover; @property (nonatomic, strong) MPFunctionsViewController *functionsViewController; +@property (nonatomic, strong) NSPopUpButton *syntaxErrorsPopUpButton; +@property (nonatomic, strong) NSTextField *mathErrorTextField; + @property (nonatomic, strong) NSTimer *caretTimer; @property (nonatomic) NSTimeInterval caretBlinkRate; @property (nonatomic) BOOL caretVisible; @@ -336,7 +341,7 @@ _expressionStorage = expressionStorage; [self initializeButtons]; - [self initializeErrorMessageTextField]; + [self initializeErrorsViews]; self.selection = [[MPRangePath alloc] initWithRange:NSMakeRange(0, 0)]; self.caretBlinkRate = 1.0; @@ -374,16 +379,27 @@ constant:0]]; } -- (void)initializeErrorMessageTextField +- (void)initializeErrorsViews { - NSTextField *errorLabel = self.errorMessageTextField; - [self addSubview:errorLabel]; - NSDictionary *variableBindings = NSDictionaryOfVariableBindings(errorLabel); - [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[errorLabel]" + + NSPopUpButton *syntaxErrors = self.syntaxErrorsPopUpButton; + NSTextField *mathError = self.mathErrorTextField; + [self addSubview:syntaxErrors]; + [self addSubview:mathError]; + NSDictionary *variableBindings = NSDictionaryOfVariableBindings(syntaxErrors, mathError); + [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[syntaxErrors]" options:0 metrics:nil views:variableBindings]]; - [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[errorLabel]-10-|" + [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[syntaxErrors]-10-|" + options:0 + metrics:nil + views:variableBindings]]; + [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[mathError]" + options:0 + metrics:nil + views:variableBindings]]; + [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[mathError]-10-|" options:0 metrics:nil views:variableBindings]]; @@ -405,6 +421,42 @@ self.needsDisplay = YES; } +- (void)setSyntaxErrors:(NSArray *)syntaxErrors +{ + _syntaxErrors = syntaxErrors; + [self.syntaxErrorsPopUpButton removeAllItems]; + if (syntaxErrors.count == 0) { + self.syntaxErrorsPopUpButton.hidden = YES; + return; + } + self.syntaxErrorsPopUpButton.hidden = NO; + [self.syntaxErrorsPopUpButton addItemWithTitle:@""]; + if (syntaxErrors.count == 1) { + NSString *title = NSLocalizedString(@"1 Syntax Error", nil); + NSDictionary *attributes = @{NSForegroundColorAttributeName: [NSColor redColor], + NSFontAttributeName: [NSFont boldSystemFontOfSize:12.0]}; + NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString:title attributes:attributes]; + [self.syntaxErrorsPopUpButton itemAtIndex:0].attributedTitle = attributedTitle; + } else { + NSString *title = [NSString stringWithFormat:NSLocalizedString(@"%ld Syntax Errors", nil), syntaxErrors.count]; + NSDictionary *attributes = @{NSForegroundColorAttributeName: [NSColor redColor], + NSFontAttributeName: [NSFont boldSystemFontOfSize:12.0]}; + NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString:title attributes:attributes]; + [self.syntaxErrorsPopUpButton itemAtIndex:0].attributedTitle = attributedTitle; + } + NSUInteger index = 0; + for (NSError *error in syntaxErrors) { + [self.syntaxErrorsPopUpButton addItemWithTitle:error.localizedDescription]; + index++; + } +} + +- (void)setMathError:(NSError *)mathError +{ + _mathError = mathError; + self.mathErrorTextField.stringValue = mathError != nil ? mathError.localizedDescription : @""; +} + - (NSButton *)radiansDegreesButton { if (!_radiansDegreesButton) { @@ -447,19 +499,46 @@ return _functionsButton; } -- (NSTextField *)errorMessageTextField +- (NSPopUpButton *)syntaxErrorsPopUpButton { - if (!_errorMessageTextField) { - NSTextField *label = [[NSTextField alloc] initWithFrame:NSZeroRect]; - label.textColor = [NSColor redColor]; - label.translatesAutoresizingMaskIntoConstraints = NO; - label.bezeled = NO; - label.drawsBackground = NO; - label.editable = NO; - label.selectable = NO; - _errorMessageTextField = label; + if (!_syntaxErrorsPopUpButton) { + NSPopUpButton *button = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 26) + pullsDown:YES]; + button.translatesAutoresizingMaskIntoConstraints = NO; + [button.cell setArrowPosition:NSPopUpArrowAtBottom]; + button.bezelStyle = NSRecessedBezelStyle; + button.buttonType = NSPushOnPushOffButton; + button.showsBorderOnlyWhileMouseInside = YES; + button.font = [NSFont boldSystemFontOfSize:12.0]; + button.hidden = YES; + button.target = self; + button.action = @selector(didSelectError:); + _syntaxErrorsPopUpButton = button; } - return _errorMessageTextField; + return _syntaxErrorsPopUpButton; +} + +- (NSTextField *)mathErrorTextField +{ + if (!_mathErrorTextField) { + NSTextField *textField = [[NSTextField alloc] init]; + textField.translatesAutoresizingMaskIntoConstraints = NO; + textField.bordered = NO; + textField.selectable = NO; + textField.editable = NO; + textField.textColor = [NSColor redColor]; + textField.font = [NSFont boldSystemFontOfSize:12.0]; + _mathErrorTextField = textField; + } + return _mathErrorTextField; +} + +- (void)didSelectError:(NSPopUpButton *)sender +{ + NSError *error = self.syntaxErrors[sender.indexOfSelectedItem-1]; + NSIndexPath *pathToExpression = error.userInfo[MPPathToExpressionKey]; + pathToExpression = [pathToExpression indexPathByAddingIndex:[error.userInfo[MPErrorIndexKey] integerValue]]; + self.selection = MPMakeRangePath(pathToExpression, 0); } #pragma mark Actions