Improved Selection Algorithm
This commit is contained in:
@@ -6,8 +6,6 @@
|
|||||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#warning X-Origin is not working yet
|
|
||||||
|
|
||||||
#import "MPExpressionView.h"
|
#import "MPExpressionView.h"
|
||||||
#import "MPExpressionStorage.h"
|
#import "MPExpressionStorage.h"
|
||||||
#import "MPExpressionLayout.h"
|
#import "MPExpressionLayout.h"
|
||||||
@@ -18,6 +16,8 @@
|
|||||||
|
|
||||||
#import "MPSumFunction.h"
|
#import "MPSumFunction.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPExpressionView ()
|
@interface MPExpressionView ()
|
||||||
|
|
||||||
@property (nonatomic, weak) NSButton *functionsButton;
|
@property (nonatomic, weak) NSButton *functionsButton;
|
||||||
@@ -28,23 +28,37 @@
|
|||||||
|
|
||||||
@property (nonatomic, getter = isSelectionModifyingStart) BOOL selectionModifyingStart;
|
@property (nonatomic, getter = isSelectionModifyingStart) BOOL selectionModifyingStart;
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSIndexPath *mouseAnchor;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPExpressionView (MPDrawing)
|
@interface MPExpressionView (MPDrawing)
|
||||||
|
|
||||||
- (NSPoint)expressionOrigin;
|
- (NSPoint)expressionOrigin;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPExpressionView (MPSelection)
|
@interface MPExpressionView (MPSelection)
|
||||||
- (void)restartCaretTimer;
|
- (void)restartCaretTimer;
|
||||||
- (void)updateCaret:(NSTimer *)timer;
|
- (void)updateCaret:(NSTimer *)timer;
|
||||||
|
|
||||||
- (NSRect)selectionRect;
|
- (NSRect)selectionRect;
|
||||||
|
|
||||||
- (void)selectRangePathWithLocation:(NSIndexPath *)location length:(NSUInteger)length;
|
- (NSIndexPath *)selectionToTheRightOf:(NSIndexPath *)selection
|
||||||
|
selectWords:(BOOL)flag;
|
||||||
|
- (NSIndexPath *)selectionToTheLeftOf:(NSIndexPath *)selection
|
||||||
|
selectWords:(BOOL)flag;
|
||||||
|
|
||||||
|
- (MPRangePath *)rangePathEnclosingAnchor:(NSIndexPath *)anchor
|
||||||
|
newSelection:(NSIndexPath *)newSelection;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPExpressionView (MPDrawing)
|
@implementation MPExpressionView (MPDrawing)
|
||||||
|
|
||||||
- (NSPoint)expressionOrigin
|
- (NSPoint)expressionOrigin
|
||||||
@@ -56,6 +70,8 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPExpressionView (MPSelection)
|
@implementation MPExpressionView (MPSelection)
|
||||||
|
|
||||||
- (void)restartCaretTimer
|
- (void)restartCaretTimer
|
||||||
@@ -85,22 +101,64 @@
|
|||||||
return selectionRect;
|
return selectionRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)selectRangePathWithLocation:(NSIndexPath *)location length:(NSUInteger)length
|
- (NSIndexPath *)selectionToTheRightOf:(NSIndexPath *)selection
|
||||||
|
selectWords:(BOOL)flag
|
||||||
{
|
{
|
||||||
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[location indexPathByRemovingLastIndex]];
|
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[selection indexPathByRemovingLastIndex]];
|
||||||
NSUInteger locationIndex = location.lastIndex;
|
NSUInteger locationInTarget = selection.lastIndex;
|
||||||
if (locationIndex > targetExpression.length) {
|
NSUInteger locationInElement;
|
||||||
locationIndex = targetExpression.length;
|
NSUInteger targetElementIndex = [targetExpression indexOfElementAtSymbolLocation:locationInTarget
|
||||||
|
offset:&locationInElement];
|
||||||
|
id<MPExpressionElement> targetElement;
|
||||||
|
if (targetElementIndex < targetExpression.numberOfElements) {
|
||||||
|
targetElement = [targetExpression elementAtIndex:targetElementIndex];
|
||||||
}
|
}
|
||||||
NSUInteger lastSelectedIndex = location.lastIndex + length;
|
if (locationInElement == 0 && !flag) {
|
||||||
if (lastSelectedIndex > targetExpression.length) {
|
NSLog(@"Move to next");
|
||||||
lastSelectedIndex = targetExpression.length;
|
// Move to next element
|
||||||
|
} else if (locationInTarget < targetExpression.length) {
|
||||||
|
if (flag) {
|
||||||
|
locationInTarget = [targetExpression locationOfElementAtIndex:targetElementIndex+1];
|
||||||
|
} else {
|
||||||
|
locationInTarget++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.selection = MPMakeRangePath([location indexPathByReplacingLastIndexWithIndex:locationIndex],lastSelectedIndex - locationIndex);
|
return [selection indexPathByReplacingLastIndexWithIndex:locationInTarget];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSIndexPath *)selectionToTheLeftOf:(NSIndexPath *)selection
|
||||||
|
selectWords:(BOOL)flag
|
||||||
|
{
|
||||||
|
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[selection indexPathByRemovingLastIndex]];
|
||||||
|
NSUInteger locationInTarget = selection.lastIndex;
|
||||||
|
NSUInteger locationInElement;
|
||||||
|
NSUInteger targetElementIndex = [targetExpression indexOfElementAtSymbolLocation:locationInTarget
|
||||||
|
offset:&locationInElement];
|
||||||
|
if (locationInElement == 0 && !flag) {
|
||||||
|
// Move to previous element
|
||||||
|
} else if (locationInTarget > 0) {
|
||||||
|
if (flag) {
|
||||||
|
if (locationInElement == 0) {
|
||||||
|
targetElementIndex--;
|
||||||
|
}
|
||||||
|
locationInTarget = [targetExpression locationOfElementAtIndex:targetElementIndex];
|
||||||
|
} else {
|
||||||
|
locationInTarget--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [selection indexPathByReplacingLastIndexWithIndex:locationInTarget];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (MPRangePath *)rangePathEnclosingAnchor:(NSIndexPath *)anchor
|
||||||
|
newSelection:(NSIndexPath *)newSelection
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPExpressionView
|
@implementation MPExpressionView
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
@@ -125,7 +183,7 @@
|
|||||||
- (void)initializeExpressionView
|
- (void)initializeExpressionView
|
||||||
{
|
{
|
||||||
// Setup the Expression Storage
|
// Setup the Expression Storage
|
||||||
MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithElements:@[@"12345", [[MPSumFunction alloc] init], [[MPSumFunction alloc] init]]];
|
MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithElements:@[@"12345", [[MPSumFunction alloc] init]]];
|
||||||
expressionStorage.expressionView = self;
|
expressionStorage.expressionView = self;
|
||||||
_expressionStorage = expressionStorage;
|
_expressionStorage = expressionStorage;
|
||||||
|
|
||||||
@@ -207,7 +265,7 @@
|
|||||||
{
|
{
|
||||||
NSString *characters = theEvent.characters;
|
NSString *characters = theEvent.characters;
|
||||||
NSMutableCharacterSet *allowedCharacters = [NSMutableCharacterSet alphanumericCharacterSet];
|
NSMutableCharacterSet *allowedCharacters = [NSMutableCharacterSet alphanumericCharacterSet];
|
||||||
[allowedCharacters addCharactersInString:@"+-*/= "];
|
[allowedCharacters addCharactersInString:@"+-*= "];
|
||||||
if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) {
|
if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) {
|
||||||
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
|
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
|
||||||
[targetExpression replaceSymbolsInRange:self.selection.rangeAtLastIndex withElements:@[characters]];
|
[targetExpression replaceSymbolsInRange:self.selection.rangeAtLastIndex withElements:@[characters]];
|
||||||
@@ -222,9 +280,9 @@
|
|||||||
if (self.selection.length > 0) {
|
if (self.selection.length > 0) {
|
||||||
self.selection = MPMakeRangePath(self.selection.maxRangePath, 0);
|
self.selection = MPMakeRangePath(self.selection.maxRangePath, 0);
|
||||||
} else {
|
} else {
|
||||||
NSIndexPath *newSelectionLocation = [self.selection.location indexPathByIncrementingLastIndex];
|
NSIndexPath *newSelectionLocation = [self selectionToTheRightOf:self.selection.location
|
||||||
[self selectRangePathWithLocation:newSelectionLocation
|
selectWords:NO];
|
||||||
length:0];
|
self.selection = MPMakeRangePath(newSelectionLocation, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,48 +290,27 @@
|
|||||||
{
|
{
|
||||||
if (self.selection.length > 0) {
|
if (self.selection.length > 0) {
|
||||||
self.selection = MPMakeRangePath(self.selection.location, 0);
|
self.selection = MPMakeRangePath(self.selection.location, 0);
|
||||||
[self selectRangePathWithLocation:self.selection.location length:0];
|
|
||||||
} else {
|
} else {
|
||||||
NSUInteger selectionIndex = self.selection.location.lastIndex;
|
NSIndexPath *newSelectionLocation = [self selectionToTheLeftOf:self.selection.location
|
||||||
if (selectionIndex > 0) {
|
selectWords:NO];
|
||||||
--selectionIndex;
|
self.selection = MPMakeRangePath(newSelectionLocation, 0);
|
||||||
}
|
|
||||||
NSIndexPath *newSelectionLocation = [self.selection.location indexPathByReplacingLastIndexWithIndex:selectionIndex];
|
|
||||||
[self selectRangePathWithLocation:newSelectionLocation
|
|
||||||
length:0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)moveWordRight:(id)sender
|
- (void)moveWordRight:(id)sender
|
||||||
{
|
{
|
||||||
NSIndexPath *location;
|
NSIndexPath *location = self.selection.maxRangePath;
|
||||||
if (self.selection.length > 0) {
|
NSIndexPath *newSelectionLocation = [self selectionToTheRightOf:location
|
||||||
location = self.selection.maxRangePath;
|
selectWords:YES];
|
||||||
} else {
|
self.selection = MPMakeRangePath(newSelectionLocation, 0);
|
||||||
location = self.selection.location;
|
|
||||||
}
|
|
||||||
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[location indexPathByRemovingLastIndex]];
|
|
||||||
NSUInteger locationInTarget = NSMaxRange(self.selection.rangeAtLastIndex);
|
|
||||||
NSUInteger offset;
|
|
||||||
NSUInteger elementIndex = [targetExpression indexOfElementAtSymbolLocation:locationInTarget
|
|
||||||
offset:&offset];
|
|
||||||
NSUInteger newLocation = locationInTarget + [targetExpression elementAtIndex:elementIndex].length - offset;
|
|
||||||
[self selectRangePathWithLocation:[location indexPathByReplacingLastIndexWithIndex:newLocation]
|
|
||||||
length:0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)moveWordLeft:(id)sender
|
- (void)moveWordLeft:(id)sender
|
||||||
{
|
{
|
||||||
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
|
NSIndexPath *location = self.selection.location;
|
||||||
NSUInteger offset;
|
NSIndexPath *newSelectionLocation = [self selectionToTheLeftOf:location
|
||||||
NSUInteger elementIndex = [targetExpression indexOfElementAtSymbolLocation:self.selection.location.lastIndex offset:&offset];
|
selectWords:YES];
|
||||||
NSUInteger newLocation = self.selection.location.lastIndex;
|
self.selection = MPMakeRangePath(newSelectionLocation, 0);
|
||||||
if (offset > 0) {
|
|
||||||
newLocation -= newLocation - offset > 0 ? offset : newLocation;
|
|
||||||
} else if (newLocation > 0) {
|
|
||||||
newLocation -= [targetExpression elementAtIndex:elementIndex-1].length;
|
|
||||||
}
|
|
||||||
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:newLocation], 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)moveToBeginningOfLine:(id)sender
|
- (void)moveToBeginningOfLine:(id)sender
|
||||||
@@ -292,17 +329,16 @@
|
|||||||
if (self.selection.length == 0) {
|
if (self.selection.length == 0) {
|
||||||
self.selectionModifyingStart = YES;
|
self.selectionModifyingStart = YES;
|
||||||
}
|
}
|
||||||
NSUInteger start = self.selection.location.lastIndex;
|
NSIndexPath *location = self.selection.location;
|
||||||
NSUInteger length = self.selection.length;
|
NSIndexPath *maxLocation = self.selection.maxRangePath;
|
||||||
if (self.selectionModifyingStart) {
|
if (self.selectionModifyingStart) {
|
||||||
if (start > 0) {
|
location = [self selectionToTheLeftOf:location
|
||||||
--start;
|
selectWords:NO];
|
||||||
++length;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
--length;
|
maxLocation = [self selectionToTheLeftOf:maxLocation
|
||||||
|
selectWords:NO];
|
||||||
}
|
}
|
||||||
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:start], length);
|
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)moveRightAndModifySelection:(id)sender
|
- (void)moveRightAndModifySelection:(id)sender
|
||||||
@@ -310,26 +346,57 @@
|
|||||||
if (self.selection.length == 0) {
|
if (self.selection.length == 0) {
|
||||||
self.selectionModifyingStart = NO;
|
self.selectionModifyingStart = NO;
|
||||||
}
|
}
|
||||||
NSUInteger start = self.selection.location.lastIndex;
|
NSIndexPath *location = self.selection.location;
|
||||||
NSUInteger length = self.selection.length;
|
NSIndexPath *maxLocation = self.selection.maxRangePath;
|
||||||
|
|
||||||
if (self.selectionModifyingStart) {
|
if (self.selectionModifyingStart) {
|
||||||
++start;
|
location = [self selectionToTheRightOf:location
|
||||||
--length;
|
selectWords:NO];
|
||||||
} else {
|
} else {
|
||||||
++length;
|
maxLocation = [self selectionToTheRightOf:maxLocation
|
||||||
|
selectWords:NO];
|
||||||
}
|
}
|
||||||
[self selectRangePathWithLocation:[self.selection.location indexPathByReplacingLastIndexWithIndex:start]
|
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
|
||||||
length:length];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)moveWordRightAndModifySelection:(id)sender
|
- (void)moveWordRightAndModifySelection:(id)sender
|
||||||
{
|
{
|
||||||
#warning Unimplemented Method
|
if (self.selection.length == 0) {
|
||||||
|
self.selectionModifyingStart = NO;
|
||||||
|
}
|
||||||
|
NSIndexPath *location = self.selection.location;
|
||||||
|
NSIndexPath *maxLocation = self.selection.maxRangePath;
|
||||||
|
if (self.selectionModifyingStart) {
|
||||||
|
location = [self selectionToTheRightOf:location
|
||||||
|
selectWords:YES];
|
||||||
|
if (location.lastIndex > maxLocation.lastIndex) {
|
||||||
|
location = [location indexPathByReplacingLastIndexWithIndex:maxLocation.lastIndex];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
maxLocation = [self selectionToTheRightOf:maxLocation
|
||||||
|
selectWords:YES];
|
||||||
|
}
|
||||||
|
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)moveWordLeftAndModifySelection:(id)sender
|
- (void)moveWordLeftAndModifySelection:(id)sender
|
||||||
{
|
{
|
||||||
#warning Unimplemented Method
|
if (self.selection.length == 0) {
|
||||||
|
self.selectionModifyingStart = YES;
|
||||||
|
}
|
||||||
|
NSIndexPath *location = self.selection.location;
|
||||||
|
NSIndexPath *maxLocation = self.selection.maxRangePath;
|
||||||
|
if (self.selectionModifyingStart) {
|
||||||
|
location = [self selectionToTheLeftOf:location
|
||||||
|
selectWords:YES];
|
||||||
|
} else {
|
||||||
|
maxLocation = [self selectionToTheLeftOf:maxLocation
|
||||||
|
selectWords:YES];
|
||||||
|
if (maxLocation.lastIndex < location.lastIndex) {
|
||||||
|
maxLocation = [maxLocation indexPathByReplacingLastIndexWithIndex:location.lastIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)selectAll:(id)sender
|
- (void)selectAll:(id)sender
|
||||||
@@ -359,9 +426,23 @@
|
|||||||
pointInView.x -= expressionOrigin.x;
|
pointInView.x -= expressionOrigin.x;
|
||||||
pointInView.y -= expressionOrigin.y;
|
pointInView.y -= expressionOrigin.y;
|
||||||
NSIndexPath *selectionPath = [self.expressionStorage.rootLayout indexPathForMousePoint:pointInView];
|
NSIndexPath *selectionPath = [self.expressionStorage.rootLayout indexPathForMousePoint:pointInView];
|
||||||
|
self.mouseAnchor = selectionPath;
|
||||||
self.selection = MPMakeRangePath(selectionPath, 0);
|
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 rangePathEnclosingAnchor:self.mouseAnchor
|
||||||
|
newSelection:mouseSelectionPath];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark Drawing Methods
|
#pragma mark Drawing Methods
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
- (void)drawRect:(NSRect)dirtyRect
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user