Archived
1

Model Redesign: Added Reference Frames

Added Inverse Functions
UI Redesign
Cleaned Code
This commit is contained in:
Kim Wittenburg
2014-10-07 20:25:54 +02:00
parent 8f2f773909
commit 82259f87e2
40 changed files with 1124 additions and 998 deletions

View File

@@ -11,8 +11,11 @@
#import "MPExpressionLayout.h"
#import "MPFunctionLayout.h"
#import "MPPowerFunction.h"
#import "MPRangePath.h"
#import "MPMathRules.h"
#import "NSIndexPath+MPAdditions.h"
#import "MPSumFunction.h"
@@ -24,10 +27,14 @@
@interface MPExpressionView ()
@property (nonatomic, strong) NSButton *radiansDegreesButton;
@property (nonatomic, strong) NSButton *functionsButton;
@property (nonatomic, strong) NSPopover *functionsPopover;
@property (nonatomic, strong) MPFunctionsViewController *functionsViewController;
@property (nonatomic, strong) NSTextField *errorLabel;
@property (nonatomic, strong) NSTimer *caretTimer;
@property (nonatomic) NSTimeInterval caretBlinkRate;
@property (nonatomic) BOOL caretVisible;
@@ -117,20 +124,22 @@
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath];
NSUInteger locationInTarget = selectionPath.lastIndex;
NSUInteger locationInElement;
NSUInteger targetElementIndex = [targetExpression indexOfElementAtLocation:locationInTarget
offset:&locationInElement];
NSUInteger targetElementIndex = [targetExpression convertIndex:locationInTarget
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame
offset:&locationInElement];
id<MPExpressionElement> targetElement;
// There is only a target element if the selection is not the last location in an expression
if (targetElementIndex < targetExpression.numberOfElements) {
if (targetElementIndex < targetExpression.countElements) {
targetElement = [targetExpression elementAtIndex:targetElementIndex];
}
if (!selectWords && !extendingSelection && (locationInElement == 0 || locationInTarget == targetExpression.length)) {
if (!selectWords && !extendingSelection && (locationInElement == 0 || locationInTarget == targetExpression.countSymbols)) {
// First or last index in an element or expression
// Last element in the expression
if (locationInTarget == targetExpression.length) {
if (locationInTarget == targetExpression.countSymbols) {
// The selection is inside a function and should proceed
if (selectionPath.length > 1) {
NSIndexPath *functionPath = [[selectionPath indexPathByRemovingLastIndex] indexPathByRemovingLastIndex];
@@ -141,7 +150,9 @@
// The function is to be exited
if (newChildIndex == NSNotFound) {
targetExpression = [self.expressionStorage elementAtIndexPath:[functionPath indexPathByRemovingLastIndex]];
NSUInteger functionLocationInExpression = [targetExpression locationOfElementAtIndex:functionPath.lastIndex];
NSUInteger functionLocationInExpression = [targetExpression convertIndex:functionPath.lastIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
return [functionPath indexPathByReplacingLastIndexWithIndex:functionLocationInExpression+1];
} else {
return [[targetExpressionPath indexPathByReplacingLastIndexWithIndex:newChildIndex] indexPathByAddingIndex:0];
@@ -159,9 +170,11 @@
return [[targetFunctionPath indexPathByAddingIndex:leadingChildIndex] indexPathByAddingIndex:0];
}
}
} else if (locationInTarget < targetExpression.length) {
} else if (locationInTarget < targetExpression.countSymbols) {
if (selectWords) {
locationInTarget = [targetExpression locationOfElementAtIndex:targetElementIndex+1];
locationInTarget = [targetExpression convertIndex:targetElementIndex+1
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
} else {
locationInTarget++;
}
@@ -177,8 +190,10 @@
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath];
NSUInteger locationInTarget = selectionPath.lastIndex;
NSUInteger locationInElement;
NSUInteger targetElementIndex = [targetExpression indexOfElementAtLocation:locationInTarget
offset:&locationInElement];
NSUInteger targetElementIndex = [targetExpression convertIndex:locationInTarget
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame
offset:&locationInElement];
NSUInteger previousElementIndex = targetElementIndex - (locationInElement == 0 ? 1 : 0);
id<MPExpressionElement> previousElement;
@@ -198,12 +213,14 @@
// The function is to be exited
if (newChildIndex == NSNotFound) {
targetExpression = [self.expressionStorage elementAtIndexPath:[functionPath indexPathByRemovingLastIndex]];
NSUInteger functionLocationInExpression = [targetExpression locationOfElementAtIndex:functionPath.lastIndex];
NSUInteger functionLocationInExpression = [targetExpression convertIndex:functionPath.lastIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
return [functionPath indexPathByReplacingLastIndexWithIndex:functionLocationInExpression];
} else {
targetExpressionPath = [targetExpressionPath indexPathByReplacingLastIndexWithIndex:newChildIndex];
targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath];
return [targetExpressionPath indexPathByAddingIndex:targetExpression.length];
return [targetExpressionPath indexPathByAddingIndex:targetExpression.countSymbols];
}
} // else the selection does not change
@@ -218,7 +235,7 @@
targetExpressionPath = [targetFunctionPath indexPathByAddingIndex:trailingChildIndex];
targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath];
return [targetExpressionPath indexPathByAddingIndex:targetExpression.length];
return [targetExpressionPath indexPathByAddingIndex:targetExpression.countSymbols];
}
}
} else if (locationInTarget > 0) {
@@ -226,7 +243,9 @@
if (locationInElement == 0) {
targetElementIndex--;
}
locationInTarget = [targetExpression locationOfElementAtIndex:targetElementIndex];
locationInTarget = [targetExpression convertIndex:targetElementIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
} else {
locationInTarget--;
}
@@ -258,10 +277,14 @@
NSUInteger newIndex = [newSelectionPath indexAtPosition:commonPath.length];
if (commonPath.length < anchorPath.length-1) {
anchorIndex = [closestCommonAncestor locationOfElementAtIndex:anchorIndex];
anchorIndex = [closestCommonAncestor convertIndex:anchorIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
}
if (commonPath.length < newSelectionPath.length-1) {
newIndex = [closestCommonAncestor locationOfElementAtIndex:newIndex];
newIndex = [closestCommonAncestor convertIndex:newIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
}
NSUInteger minIndex = MIN(anchorIndex, newIndex);
@@ -313,6 +336,7 @@
_expressionStorage = expressionStorage;
[self initializeButtons];
[self initializeErrorLabel];
self.selection = [[MPRangePath alloc] initWithRange:NSMakeRange(0, 0)];
self.caretBlinkRate = 1.0;
@@ -321,10 +345,23 @@
- (void)initializeButtons
{
NSButton *radiansDegreesButton = self.radiansDegreesButton;
[self addSubview:radiansDegreesButton];
NSButton *functionsButton = self.functionsButton;
[self addSubview:functionsButton];
NSDictionary *variableBindings = NSDictionaryOfVariableBindings(functionsButton);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[functionsButton]-10-|"
NSDictionary *variableBindings = NSDictionaryOfVariableBindings(radiansDegreesButton, functionsButton);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[radiansDegreesButton]"
options:0
metrics:nil
views:variableBindings]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[radiansDegreesButton]"
options:0
metrics:nil
views:variableBindings]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[functionsButton]-10-|"
options:0
metrics:nil
views:variableBindings]];
@@ -337,6 +374,21 @@
constant:0]];
}
- (void)initializeErrorLabel
{
NSTextField *errorLabel = self.errorLabel;
[self addSubview:errorLabel];
NSDictionary *variableBindings = NSDictionaryOfVariableBindings(errorLabel);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[errorLabel]"
options:0
metrics:nil
views:variableBindings]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[errorLabel]-10-|"
options:0
metrics:nil
views:variableBindings]];
}
#pragma mark Properties
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
{
@@ -356,16 +408,36 @@
- (void)setError:(MPParseError *)error
{
_error = error;
self.errorLabel.stringValue = error.localizedErrorMessage != nil ? error.localizedErrorMessage : @"";
self.needsDisplay = YES;
}
- (NSButton *)radiansDegreesButton
{
if (!_radiansDegreesButton) {
NSButton *radiansDegreesButton = [[NSButton alloc] initWithFrame:NSZeroRect];
radiansDegreesButton.translatesAutoresizingMaskIntoConstraints = NO;
radiansDegreesButton.buttonType = NSMomentaryPushInButton;
radiansDegreesButton.bordered = YES;
radiansDegreesButton.bezelStyle = NSRoundedBezelStyle;
radiansDegreesButton.imagePosition = NSNoImage;
radiansDegreesButton.alignment = NSCenterTextAlignment;
radiansDegreesButton.title = NSLocalizedString([MPMathRules sharedRules].isUsingDegrees ? @"Deg" : @"Rad", nil);
radiansDegreesButton.font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
radiansDegreesButton.target = self;
radiansDegreesButton.action = @selector(switchRadiansDegrees:);
_radiansDegreesButton = radiansDegreesButton;
}
return _radiansDegreesButton;
}
- (NSButton *)functionsButton
{
if (!_functionsButton) {
NSBundle *frameworkBundle = [NSBundle bundleForClass:[self class]];
NSImage *image = [frameworkBundle imageForResource:@"FunctionsButtonDisclosure"];
[image setName:@"FunctionsButtonDisclosure"];
NSButton *button = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
button.translatesAutoresizingMaskIntoConstraints = NO;
button.target = self;
button.action = @selector(showFunctions:);
@@ -382,7 +454,28 @@
return _functionsButton;
}
- (NSTextField *)errorLabel
{
if (!_errorLabel) {
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;
_errorLabel = label;
}
return _errorLabel;
}
#pragma mark Actions
- (void)switchRadiansDegrees:(id)sender
{
[MPMathRules sharedRules].isUsingDegrees = ![MPMathRules sharedRules].isUsingDegrees;
self.radiansDegreesButton.title = NSLocalizedString([MPMathRules sharedRules].isUsingDegrees ? @"Deg" : @"Rad", nil);
}
- (void)showFunctions:(id)sender
{
if (self.functionsPopover == nil || self.functionsViewController == nil) {
@@ -404,10 +497,14 @@
- (void)insertFunction:(MPFunction *)function
{
[self.functionsPopover close];
[self.expressionStorage replaceSymbolsInRangePath:self.selection withElements:@[function]];
[self.expressionStorage replaceItemsInRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame
withElements:@[function]];
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:[targetExpression indexOfElementAtLocation:self.selection.location.lastIndex
offset:NULL]];
NSUInteger functionElementIndex = [targetExpression convertIndex:self.selection.location.lastIndex
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame];
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:functionElementIndex];
MPFunctionLayout *functionLayout = (MPFunctionLayout *)[self.expressionStorage.rootLayout childLayoutAtIndexPath:functionPath];
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:functionLayout.indexOfLeadingChild] indexPathByAddingIndex:0], 0);
}
@@ -475,15 +572,19 @@
[self insertParenthesisFunction:nil];
return;
}
if (theEvent.keyCode == 10) {
[self insertPowerFunction];
return;
}
NSString *decimalSeparator = [NSRegularExpression escapedPatternForString:[[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator]];
NSMutableCharacterSet *allowedCharacters = [NSMutableCharacterSet alphanumericCharacterSet];
[allowedCharacters addCharactersInString:[NSString stringWithFormat:@"+-*= !%@", decimalSeparator]];
if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) {
[self.expressionStorage replaceSymbolsInRangePath:self.selection withElements:@[characters]];
[self.expressionStorage replaceItemsInRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame
withElements:@[characters]];
self.selection = MPMakeRangePath([self.selection.location indexPathByIncrementingLastIndex], 0);
} else {
[self interpretKeyEvents:@[theEvent]];
@@ -492,15 +593,37 @@
- (void)insertParenthesisFunction:(id)sender
{
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection];
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame];
MPParenthesisFunction *function = [[MPParenthesisFunction alloc] init];
function.expression = selectedElementsExpression;
[self.expressionStorage replaceSymbolsInRangePath:self.selection
withElements:@[function]];
[self.expressionStorage replaceItemsInRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame
withElements:@[function]];
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:[targetExpression indexOfElementAtLocation:self.selection.location.lastIndex
offset:NULL]];
NSUInteger functionElementIndex = [targetExpression convertIndex:self.selection.location.lastIndex
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame];
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:functionElementIndex];
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
}
- (void)insertPowerFunction
{
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame];
MPPowerFunction *function = [[MPPowerFunction alloc] init];
function.exponentExpression = selectedElementsExpression;
[self.expressionStorage replaceItemsInRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame
withElements:@[function]];
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
NSUInteger functionElementIndex = [targetExpression convertIndex:self.selection.location.lastIndex
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame];
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:functionElementIndex];
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
}
@@ -563,7 +686,7 @@
- (void)moveToEndOfLine:(id)sender
{
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:targetExpression.length], 0);
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:targetExpression.countSymbols], 0);
}
- (void)moveLeftAndModifySelection:(id)sender
@@ -676,22 +799,27 @@
- (void)selectAll:(id)sender
{
NSIndexPath *location = [NSIndexPath indexPathWithIndex:0];
self.selection = MPMakeRangePath(location, self.expressionStorage.length);
self.selection = MPMakeRangePath(location, self.expressionStorage.countSymbols);
}
- (void)deleteBackward:(id)sender
{
if (self.selection.length > 0) {
[self.expressionStorage replaceSymbolsInRangePath:self.selection withElements:@[]];
[self.expressionStorage replaceItemsInRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame
withElements:@[]];
self.selection = MPMakeRangePath(self.selection.location, 0);
} else if (self.selection.location.lastIndex > 0) {
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
id <MPExpressionElement> elementToDelete = [targetExpression elementAtIndex:[targetExpression indexOfElementAtLocation:self.selection.location.lastIndex-1
offset:NULL]];
id<MPExpressionElement> elementToDelete = [targetExpression elementAtIndex:[targetExpression convertIndex:self.selection.location.lastIndex-1
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame]];
if ([elementToDelete isFunction]) {
self.selection = MPMakeRangePath(self.selection.location.indexPathByDecrementingLastIndex, 1);
} else {
[targetExpression replaceSymbolsInRange:NSMakeRange(self.selection.location.lastIndex-1, 1) withElements:@[]];
[targetExpression replaceItemsInRange:NSMakeRange(self.selection.location.lastIndex-1, 1)
referenceFrame:MPSymbolReferenceFrame
withElements:@[]];
self.selection = MPMakeRangePath([self.selection.location indexPathByDecrementingLastIndex], 0);
}
} else {
@@ -707,14 +835,20 @@
for (NSUInteger index = remainingIndexes.firstIndex;
index <= remainingIndexes.lastIndex;
index = [remainingIndexes indexGreaterThanIndex:index]) {
[remainder addObjectsFromArray:[function childAtIndex:index].elements];
MPExpression *expression = [function childAtIndex:index];
[remainder addObjectsFromArray:[expression allItemsInReferenceFrame:MPElementReferenceFrame]];
}
NSIndexPath *newTargetExpressionPath = [functionPath indexPathByRemovingLastIndex];
MPExpression *newTargetExpression = [self.expressionStorage elementAtIndexPath:newTargetExpressionPath];
NSIndexPath *newSelectionLocation = [functionPath indexPathByReplacingLastIndexWithIndex:[newTargetExpression locationOfElementAtIndex:functionPath.lastIndex]];
NSUInteger newSelectionElementIndex = [newTargetExpression convertIndex:functionPath.lastIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
NSIndexPath *newSelectionLocation = [functionPath indexPathByReplacingLastIndexWithIndex:newSelectionElementIndex];
[self.expressionStorage replaceSymbolsInRangePath:MPMakeRangePath(newSelectionLocation, 1) withElements:remainder];
[self.expressionStorage replaceItemsInRangePath:MPMakeRangePath(newSelectionLocation, 1)
referenceFrame:MPSymbolReferenceFrame
withElements:remainder];
self.selection = MPMakeRangePath(newSelectionLocation, 0);
}
}
@@ -762,6 +896,24 @@
yBy:expressionOrigin.y];
[transform concat];
// Draw the error
if (self.error) {
[[NSColor redColor] set];
NSRect rect = [self.expressionStorage.rootLayout boundingRectForRangePath:self.error.rangePath];
if (self.error.rangePath.length == 0) {
NSBezierPath *bezierPath = [NSBezierPath bezierPath];
[bezierPath moveToPoint:rect.origin];
[bezierPath lineToPoint:NSMakePoint(rect.origin.x - 5, rect.origin.y - 5)];
[bezierPath moveToPoint:rect.origin];
[bezierPath lineToPoint:NSMakePoint(rect.origin.x + 5, rect.origin.y - 5)];
bezierPath.lineWidth = 2.0;
[bezierPath stroke];
} else {
NSRect underlineRect = NSMakeRect(rect.origin.x, rect.origin.y + 2, rect.size.width, 2);
NSRectFill(underlineRect);
}
}
// Draw the selection
if (self.caretVisible || self.selection.length > 0) {
if (self.selection.length == 0) {