From 5aef06febe0bff7e1c6cfa78a0f52003b47f8fba Mon Sep 17 00:00:00 2001 From: Kim Wittenburg Date: Wed, 17 Dec 2014 22:05:22 +0100 Subject: [PATCH] Removed Deg/Rad Button, Replaced with Menu Actions --- MathKit/MPExpressionView.m | 415 +++++++++++++++++++------------------ 1 file changed, 211 insertions(+), 204 deletions(-) diff --git a/MathKit/MPExpressionView.m b/MathKit/MPExpressionView.m index 40a04df..a4317c4 100644 --- a/MathKit/MPExpressionView.m +++ b/MathKit/MPExpressionView.m @@ -9,7 +9,7 @@ #import "MPExpressionView.h" #import "MPExpression.h" -#import "MPExpressionElement.h" +#import "MPExpression.h" #import "MPParsedExpression.h" #import "MPPowerFunction.h" @@ -30,8 +30,6 @@ @interface MPExpressionView () -@property (nonatomic, strong) NSButton *radiansDegreesButton; - @property (nonatomic, strong) NSButton *functionsButton; @property (nonatomic, strong) NSPopover *functionsPopover; @property (nonatomic, strong) MPFunctionsViewController *functionsViewController; @@ -59,7 +57,7 @@ -@interface MPExpressionView (MPSelection) +@interface MPExpressionView (MPSelectionHelper) - (void)restartCaretTimer; - (void)updateCaret:(NSTimer *)timer; @@ -91,7 +89,7 @@ -@implementation MPExpressionView (MPSelection) +@implementation MPExpressionView (MPSelectionHelper) - (void)restartCaretTimer { @@ -313,6 +311,7 @@ +#pragma mark - @implementation MPExpressionView #pragma mark Creation Methods @@ -351,21 +350,10 @@ - (void)initializeButtons { - NSButton *radiansDegreesButton = self.radiansDegreesButton; - [self addSubview:radiansDegreesButton]; NSButton *functionsButton = self.functionsButton; [self addSubview:functionsButton]; - 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]]; + NSDictionary *variableBindings = NSDictionaryOfVariableBindings(functionsButton); [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[functionsButton]-10-|" options:0 @@ -406,7 +394,56 @@ views:variableBindings]]; } -#pragma mark Properties +#pragma mark - NSView Configuration +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (BOOL)canBecomeKeyView +{ + return YES; +} + +- (BOOL)isOpaque +{ + return YES; +} + +- (void)resetCursorRects +{ + [self addCursorRect:self.bounds + cursor:[NSCursor IBeamCursor]]; +} + +- (BOOL)becomeFirstResponder +{ + [self restartCaretTimer]; + return [super becomeFirstResponder]; +} + +- (BOOL)resignFirstResponder +{ + [self.caretTimer invalidate]; + self.caretVisible = NO; + self.needsDisplay = YES; + return [super resignFirstResponder]; +} + +- (void)setFrame:(NSRect)frameRect +{ + [self setNeedsLayout:YES]; + [super setFrame:frameRect]; +} + +- (NSSize)intrinsicContentSize +{ + NSSize size = self.expressionStorage.rootLayout.bounds.size; + size.width += 100; + return size; +} + +#pragma mark - Properties - (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage { _expressionStorage.expressionView = nil; @@ -460,25 +497,6 @@ self.mathErrorTextField.stringValue = mathError != nil ? mathError.localizedDescription : @""; } -- (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) { @@ -488,7 +506,7 @@ NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect]; button.translatesAutoresizingMaskIntoConstraints = NO; button.target = self; - button.action = @selector(showFunctions:); + button.action = @selector(toggleFunctionsPopover:); button.buttonType = NSMomentaryChangeButton; button.bezelStyle = NSShadowlessSquareBezelStyle; button.bordered = NO; @@ -546,98 +564,30 @@ self.selection = MPMakeRangePath(pathToExpression, errorRange.length); } -#pragma mark Actions -- (void)switchRadiansDegrees:(id)sender +#pragma mark - Mouse Event Handling +- (void)mouseDown:(NSEvent *)theEvent { - [MPMathRules sharedRules].isUsingDegrees = ![MPMathRules sharedRules].isUsingDegrees; - self.radiansDegreesButton.title = NSLocalizedString([MPMathRules sharedRules].isUsingDegrees ? @"Deg" : @"Rad", nil); + NSPoint pointInView = [self convertPoint:theEvent.locationInWindow + fromView:nil]; + NSPoint expressionOrigin = self.expressionOrigin; + pointInView.x -= expressionOrigin.x; + pointInView.y -= expressionOrigin.y; + NSIndexPath *selectionPath = [self.expressionStorage.rootLayout indexPathForMousePoint:pointInView]; + self.mouseAnchor = selectionPath; + self.selection = MPMakeRangePath(selectionPath, 0); } -- (void)showFunctions:(id)sender +- (void)mouseDragged:(NSEvent *)theEvent { - if (self.functionsPopover == nil || self.functionsViewController == nil) { - self.functionsViewController = [[MPFunctionsViewController alloc] init]; - self.functionsViewController.target = self; - self.functionsViewController.action = @selector(insertFunction:); - self.functionsPopover = [[NSPopover alloc] init]; - self.functionsPopover.contentViewController = self.functionsViewController; - self.functionsPopover.animates = YES; - self.functionsPopover.behavior = NSPopoverBehaviorSemitransient; - } - if (self.functionsPopover.isShown) { - [self.functionsPopover close]; - } else { - [self.functionsPopover showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxYEdge]; - } -} - -- (void)insertFunction:(MPFunction *)function -{ - [self.functionsPopover close]; - [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]; - MPFunctionLayout *functionLayout = (MPFunctionLayout *)[self.expressionStorage.rootLayout childLayoutAtIndexPath:functionPath]; - self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:functionLayout.indexOfLeadingChild] indexPathByAddingIndex:0], 0); -} - -#pragma mark NSView Stuff -- (BOOL)acceptsFirstResponder -{ - return YES; -} - -- (BOOL)canBecomeKeyView -{ - return YES; -} - -- (BOOL)becomeFirstResponder -{ - [self restartCaretTimer]; - return [super becomeFirstResponder]; -} - -- (BOOL)resignFirstResponder -{ - [self.caretTimer invalidate]; - self.caretVisible = NO; - self.needsDisplay = YES; - return [super resignFirstResponder]; -} - -- (BOOL)isFlipped -{ - return NO; -} - -- (BOOL)isOpaque -{ - return YES; -} - -- (void)setFrame:(NSRect)frameRect -{ - [self setNeedsLayout:YES]; - [super setFrame:frameRect]; -} - -- (NSSize)intrinsicContentSize -{ - NSSize size = self.expressionStorage.rootLayout.bounds.size; - size.width += 100; - return size; -} - -- (void)resetCursorRects -{ - [self addCursorRect:self.bounds - cursor:[NSCursor IBeamCursor]]; + NSPoint pointInView = [self convertPoint:theEvent.locationInWindow + fromView:nil]; + NSPoint expressionOrigin = self.expressionOrigin; + pointInView.x -= expressionOrigin.x; + pointInView.y -= expressionOrigin.y; + NSIndexPath *mouseSelectionPath = [self.expressionStorage.rootLayout indexPathForMousePoint:pointInView]; + + self.selection = [self rangePathEnclosingAnchorPath:self.mouseAnchor + newSelectionPath:mouseSelectionPath]; } #pragma mark Key Event Handling @@ -679,6 +629,57 @@ } } +#pragma mark - Actions +- (void)switchToDegrees:(id)sender +{ + [MPMathRules sharedRules].isUsingDegrees = YES; +} + +- (void)switchToRadians:(id)sender +{ + [MPMathRules sharedRules].isUsingDegrees = NO; +} + +- (void)switchRadiansDegrees:(id)sender +{ + [MPMathRules sharedRules].isUsingDegrees = ![MPMathRules sharedRules].isUsingDegrees; +} + +- (IBAction)toggleFunctionsPopover:(id)sender +{ + if (self.functionsPopover == nil || self.functionsViewController == nil) { + self.functionsViewController = [[MPFunctionsViewController alloc] init]; + self.functionsViewController.target = self; + self.functionsViewController.action = @selector(insertFunction:); + self.functionsPopover = [[NSPopover alloc] init]; + self.functionsPopover.contentViewController = self.functionsViewController; + self.functionsPopover.animates = YES; + self.functionsPopover.behavior = NSPopoverBehaviorSemitransient; + } + if (self.functionsPopover.isShown) { + [self.functionsPopover close]; + } else { + [self.functionsPopover showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxYEdge]; + } +} + +- (void)insertFunction:(MPFunction *)function +{ + [self.functionsPopover close]; + [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]; + MPFunctionLayout *functionLayout = (MPFunctionLayout *)[self.expressionStorage.rootLayout childLayoutAtIndexPath:functionPath]; + self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:functionLayout.indexOfLeadingChild] indexPathByAddingIndex:0], 0); +} + +#pragma mark Editing Actions + - (void)insertParenthesisFunction:(id)sender { MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection @@ -800,6 +801,68 @@ } } +- (void)deleteBackward:(id)sender +{ + if (self.selection.length > 0) { + [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 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 replaceItemsInRange:NSMakeRange(self.selection.location.lastIndex-1, 1) + referenceFrame:MPSymbolReferenceFrame + withElements:@[]]; + self.selection = MPMakeRangePath([self.selection.location indexPathByDecrementingLastIndex], 0); + } + } else { + NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex]; + MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath]; + if (targetExpression.parent != nil) { + NSIndexPath *functionPath = [targetExpressionPath indexPathByRemovingLastIndex]; + MPFunction *function = [self.expressionStorage elementAtIndexPath:functionPath]; + MPFunctionLayout *functionLayout = (MPFunctionLayout *)[self.expressionStorage.rootLayout childLayoutAtIndexPath:functionPath]; + NSIndexSet *remainingIndexes = [functionLayout indexesOfRemainingChildren]; + + NSMutableArray *remainder = [[NSMutableArray alloc] init]; + for (NSUInteger index = remainingIndexes.firstIndex; + index <= remainingIndexes.lastIndex; + index = [remainingIndexes indexGreaterThanIndex:index]) { + MPExpression *expression = [function childAtIndex:index]; + [remainder addObjectsFromArray:[expression allItemsInReferenceFrame:MPElementReferenceFrame]]; + } + + NSIndexPath *newTargetExpressionPath = [functionPath indexPathByRemovingLastIndex]; + MPExpression *newTargetExpression = [self.expressionStorage elementAtIndexPath:newTargetExpressionPath]; + NSUInteger newSelectionElementIndex = [newTargetExpression convertIndex:functionPath.lastIndex + fromReferenceFrame:MPElementReferenceFrame + toReferenceFrame:MPSymbolReferenceFrame]; + NSIndexPath *newSelectionLocation = [functionPath indexPathByReplacingLastIndexWithIndex:newSelectionElementIndex]; + + [self.expressionStorage replaceItemsInRangePath:MPMakeRangePath(newSelectionLocation, 1) + referenceFrame:MPSymbolReferenceFrame + withElements:remainder]; + self.selection = MPMakeRangePath(newSelectionLocation, 0); + } + } +} + +- (void)delete:(id)sender +{ + [self.expressionStorage replaceItemsInRangePath:self.selection + referenceFrame:MPSymbolReferenceFrame + withElements:@[]]; + self.selection = MPMakeRangePath(self.selection.location, 0); +} + +#pragma mark Selection Actions + - (void)moveRight:(id)sender { if (self.selection.length > 0) { @@ -966,84 +1029,6 @@ self.selection = MPMakeRangePath(location, self.expressionStorage.countSymbols); } -- (void)deleteBackward:(id)sender -{ - if (self.selection.length > 0) { - [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 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 replaceItemsInRange:NSMakeRange(self.selection.location.lastIndex-1, 1) - referenceFrame:MPSymbolReferenceFrame - withElements:@[]]; - self.selection = MPMakeRangePath([self.selection.location indexPathByDecrementingLastIndex], 0); - } - } else { - NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex]; - MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath]; - if (targetExpression.parent != nil) { - NSIndexPath *functionPath = [targetExpressionPath indexPathByRemovingLastIndex]; - MPFunction *function = [self.expressionStorage elementAtIndexPath:functionPath]; - MPFunctionLayout *functionLayout = (MPFunctionLayout *)[self.expressionStorage.rootLayout childLayoutAtIndexPath:functionPath]; - NSIndexSet *remainingIndexes = [functionLayout indexesOfRemainingChildren]; - - NSMutableArray *remainder = [[NSMutableArray alloc] init]; - for (NSUInteger index = remainingIndexes.firstIndex; - index <= remainingIndexes.lastIndex; - index = [remainingIndexes indexGreaterThanIndex:index]) { - MPExpression *expression = [function childAtIndex:index]; - [remainder addObjectsFromArray:[expression allItemsInReferenceFrame:MPElementReferenceFrame]]; - } - - NSIndexPath *newTargetExpressionPath = [functionPath indexPathByRemovingLastIndex]; - MPExpression *newTargetExpression = [self.expressionStorage elementAtIndexPath:newTargetExpressionPath]; - NSUInteger newSelectionElementIndex = [newTargetExpression convertIndex:functionPath.lastIndex - fromReferenceFrame:MPElementReferenceFrame - toReferenceFrame:MPSymbolReferenceFrame]; - NSIndexPath *newSelectionLocation = [functionPath indexPathByReplacingLastIndexWithIndex:newSelectionElementIndex]; - - [self.expressionStorage replaceItemsInRangePath:MPMakeRangePath(newSelectionLocation, 1) - referenceFrame:MPSymbolReferenceFrame - withElements:remainder]; - self.selection = MPMakeRangePath(newSelectionLocation, 0); - } - } -} - -#pragma mark Mouse Event Handling -- (void)mouseDown:(NSEvent *)theEvent -{ - NSPoint pointInView = [self convertPoint:theEvent.locationInWindow - fromView:nil]; - NSPoint expressionOrigin = self.expressionOrigin; - pointInView.x -= expressionOrigin.x; - pointInView.y -= expressionOrigin.y; - NSIndexPath *selectionPath = [self.expressionStorage.rootLayout indexPathForMousePoint:pointInView]; - self.mouseAnchor = selectionPath; - self.selection = MPMakeRangePath(selectionPath, 0); -} - -- (void)mouseDragged:(NSEvent *)theEvent -{ - NSPoint pointInView = [self convertPoint:theEvent.locationInWindow - fromView:nil]; - NSPoint expressionOrigin = self.expressionOrigin; - pointInView.x -= expressionOrigin.x; - pointInView.y -= expressionOrigin.y; - NSIndexPath *mouseSelectionPath = [self.expressionStorage.rootLayout indexPathForMousePoint:pointInView]; - - self.selection = [self rangePathEnclosingAnchorPath:self.mouseAnchor - newSelectionPath:mouseSelectionPath]; -} - #pragma mark Drawing Methods - (void)drawRect:(NSRect)dirtyRect { @@ -1078,4 +1063,26 @@ [self.expressionStorage.rootLayout drawAtPoint:expressionOrigin]; } + +#pragma mark - User Interface Validations + +- (BOOL)validateUserInterfaceItem:(id)anItem +{ + BOOL degrees = [MPMathRules sharedRules].isUsingDegrees; + if (anItem.action == @selector(switchToRadians:)) { + id anObject = anItem; + if ([anObject respondsToSelector:@selector(setState:)]) { + [anObject setState:degrees?NSOffState:NSOnState]; + } + return YES; + } else if (anItem.action == @selector(switchToDegrees:)) { + id anObject = anItem; + if ([anObject respondsToSelector:@selector(setState:)]) { + [anObject setState:degrees?NSOnState:NSOffState]; + } + return YES; + } + return YES; +} + @end