Archived
1

Improved Model

Added Keyboard Selection Support
Added Mouse Selection Support
Added Keyboard Editing Support
Corrected Some Bugs
Abstracted the Layout System further
Added Functions Button (test)
This commit is contained in:
Kim Wittenburg
2014-08-31 15:41:17 +02:00
parent 9aa4bca234
commit 4a3ea0cede
23 changed files with 885 additions and 262 deletions

View File

@@ -6,34 +6,109 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#warning X-Origin is not working yet
#import "MPExpressionView.h"
#import "MPExpressionStorage.h"
#import "MPExpressionLayout.h"
#import "MPRangePath.h"
#import "NSIndexPath+MPAdditions.h"
#import "MPSumFunction.h"
@interface MPExpressionView (MPCursor)
@interface MPExpressionView ()
@property (nonatomic, weak) NSButton *functionsButton;
@property (nonatomic, strong) NSTimer *caretTimer;
@property (nonatomic) NSTimeInterval caretBlinkRate;
@property (nonatomic) BOOL caretVisible;
@property (nonatomic, getter = isSelectionModifyingStart) BOOL selectionModifyingStart;
@end
@interface MPExpressionView (MPDrawing)
- (NSPoint)expressionOrigin;
@end
@interface MPExpressionView (MPSelection)
- (void)restartCaretTimer;
- (void)updateCaret:(NSTimer *)timer;
- (NSRect)selectionRect;
- (void)selectRangePathWithLocation:(NSIndexPath *)location length:(NSUInteger)length;
@end
@implementation MPExpressionView (MPDrawing)
- (NSPoint)expressionOrigin
{
NSRect expressionBounds = [self.expressionStorage.rootLayout bounds];
CGFloat y = (self.bounds.size.height - expressionBounds.size.height) / 2 + fabs(expressionBounds.origin.y);
return NSMakePoint(self.bounds.origin.x, self.bounds.origin.y + y);
}
@end
@implementation MPExpressionView (MPSelection)
- (void)restartCaretTimer
{
if (self.caretTimer) {
if ([self.caretTimer isValid]) {
[self.caretTimer invalidate];
}
}
self.caretTimer = [NSTimer scheduledTimerWithTimeInterval:self.caretBlinkRate/2 target:self selector:@selector(updateCaret:) userInfo:nil repeats:YES];
self.caretVisible = NO;
[self updateCaret:self.caretTimer];
}
- (void)updateCaret:(NSTimer *)timer
{
self.caretVisible = !self.caretVisible;
self.needsDisplay = YES;
}
- (NSRect)selectionRect
{
NSRect selectionRect = [self.expressionStorage.rootLayout boundingRectForRangePath:self.selection];
if (self.selection.length == 0) {
selectionRect.size.width = 1;
}
return selectionRect;
}
- (void)selectRangePathWithLocation:(NSIndexPath *)location length:(NSUInteger)length
{
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[location indexPathByRemovingLastIndex]];
NSUInteger locationIndex = location.lastIndex;
if (locationIndex > targetExpression.length) {
locationIndex = targetExpression.length;
}
NSUInteger lastSelectedIndex = location.lastIndex + length;
if (lastSelectedIndex > targetExpression.length) {
lastSelectedIndex = targetExpression.length;
}
self.selection = MPMakeRangePath([location indexPathByReplacingLastIndexWithIndex:locationIndex],lastSelectedIndex - locationIndex);
}
@end
@implementation MPExpressionView
#pragma mark Creation Methods
- (instancetype)init
{
self = [super init];
if (self) {
[self initializeObjects];
}
return self;
}
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self initializeObjects];
[self initializeExpressionView];
}
return self;
}
@@ -42,25 +117,44 @@
{
self = [super initWithCoder:aDecoder];
if (self) {
[self initializeObjects];
[self initializeExpressionView];
}
return self;
}
- (void)initializeObjects
- (void)initializeExpressionView
{
MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithExpressionView:self elements:@[@"12345", [[MPSumFunction alloc] init], [[MPSumFunction alloc] init]]];
MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithElements:@[@"12345", [[MPSumFunction alloc] init], [[MPSumFunction alloc] init]]];
expressionStorage.expressionView = self;
_expressionStorage = expressionStorage;
_caretLocation = [[NSIndexPath alloc] initWithIndex:0];
NSRect frame = NSMakeRect(10, 10, 500, 500);
NSButton *button = [[NSButton alloc] initWithFrame:frame];
button.buttonType = NSSwitchButton;
[button setTitle:@"Functions"];
self.functionsButton = button;
[self addSubview:self.functionsButton];
self.selection = [[MPRangePath alloc] initWithRange:NSMakeRange(0, 0)];
self.caretBlinkRate = 1.0;
[self restartCaretTimer];
}
#pragma mark Properties
- (BOOL)isFlipped
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
{
return NO;
_expressionStorage.expressionView = nil;
_expressionStorage = expressionStorage;;
_expressionStorage.expressionView = self;
[self invalidateIntrinsicContentSize];
}
- (void)setSelection:(MPRangePath *)selection
{
_selection = selection;
[self restartCaretTimer];
self.needsDisplay = YES;
}
#pragma mark NSView Stuff
- (BOOL)acceptsFirstResponder
{
return YES;
@@ -71,29 +165,226 @@
return YES;
}
#pragma mark Event Handling
- (BOOL)isFlipped
{
return NO;
}
- (BOOL)isOpaque
{
return YES;
}
- (void)layout
{
NSSize buttonSize = [self.functionsButton fittingSize];
self.functionsButton.frame = NSMakeRect(self.bounds.size.width - buttonSize.width,
(self.bounds.size.height - buttonSize.height) / 2,
buttonSize.width,
buttonSize.height);
[super layout];
}
- (NSSize)intrinsicContentSize
{
// return NSMakeSize(500, 500);
// return self.bounds.size;
return self.expressionStorage.rootLayout.bounds.size;
}
- (void)resetCursorRects
{
[self addCursorRect:self.bounds
cursor:[NSCursor IBeamCursor]];
}
#pragma mark Key Event Handling
- (void)keyDown:(NSEvent *)theEvent
{
[self interpretKeyEvents:@[theEvent]];
NSString *characters = theEvent.characters;
if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:[NSCharacterSet decimalDigitCharacterSet]].length == 0) {
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
[targetExpression replaceSymbolsInRange:self.selection.rangeAtLastIndex withElements:@[characters]];
self.selection = MPMakeRangePath([self.selection.location indexPathByIncrementingLastIndex], 0);
} else {
[self interpretKeyEvents:@[theEvent]];
}
}
- (void)moveRight:(id)sender
{
[self.expressionStorage deleteElementsInRange:NSMakeRange(0, 1)];
if (self.selection.length > 0) {
self.selection = MPMakeRangePath(self.selection.maxRangePath, 0);
} else {
NSIndexPath *newSelectionLocation = [self.selection.location indexPathByIncrementingLastIndex];
[self selectRangePathWithLocation:newSelectionLocation
length:0];
}
}
- (void)moveLeft:(id)sender
{
if (self.selection.length > 0) {
self.selection = MPMakeRangePath(self.selection.location, 0);
[self selectRangePathWithLocation:self.selection.location length:0];
} else {
NSUInteger selectionIndex = self.selection.location.lastIndex;
if (selectionIndex > 0) {
--selectionIndex;
}
NSIndexPath *newSelectionLocation = [self.selection.location indexPathByReplacingLastIndexWithIndex:selectionIndex];
[self selectRangePathWithLocation:newSelectionLocation
length:0];
}
}
- (void)moveWordRight:(id)sender
{
NSIndexPath *location;
if (self.selection.length > 0) {
location = self.selection.maxRangePath;
} else {
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
{
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
NSUInteger offset;
NSUInteger elementIndex = [targetExpression indexOfElementAtSymbolLocation:self.selection.location.lastIndex offset:&offset];
NSUInteger newLocation = self.selection.location.lastIndex;
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
{
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:0], 0);
}
- (void)moveToEndOfLine:(id)sender
{
}
- (void)moveLeftAndModifySelection:(id)sender
{
if (self.selection.length == 0) {
self.selectionModifyingStart = YES;
}
NSUInteger start = self.selection.location.lastIndex;
NSUInteger length = self.selection.length;
if (self.selectionModifyingStart) {
if (start > 0) {
--start;
++length;
}
} else {
--length;
}
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:start], length);
}
- (void)moveRightAndModifySelection:(id)sender
{
if (self.selection.length == 0) {
self.selectionModifyingStart = NO;
}
NSUInteger start = self.selection.location.lastIndex;
NSUInteger length = self.selection.length;
if (self.selectionModifyingStart) {
++start;
--length;
} else {
++length;
}
[self selectRangePathWithLocation:[self.selection.location indexPathByReplacingLastIndexWithIndex:start]
length:length];
}
- (void)moveWordRightAndModifySelection:(id)sender
{
}
- (void)moveWordLeftAndModifySelection:(id)sender
{
}
- (void)selectAll:(id)sender
{
NSIndexPath *location = [NSIndexPath indexPathWithIndex:0];
self.selection = MPMakeRangePath(location, self.expressionStorage.length);
}
- (void)deleteBackward:(id)sender
{
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
if (self.selection.length > 0) {
[targetExpression replaceSymbolsInRange:self.selection.rangeAtLastIndex withElements:@[]];
self.selection = MPMakeRangePath(self.selection.location, 0);
} else if (self.selection.location.lastIndex > 0) {
[targetExpression replaceSymbolsInRange:NSMakeRange(self.selection.location.lastIndex-1, 1) withElements:@[]];
self.selection = MPMakeRangePath([self.selection.location indexPathByDecrementingLastIndex], 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.selection = MPMakeRangePath(selectionPath, 0);
}
#pragma mark Drawing Methods
- (void)drawRect:(NSRect)dirtyRect
{
// Draw the background
[super drawRect:dirtyRect];
[[NSColor whiteColor] set];
NSRectFill(self.bounds);
[[NSColor blackColor] set];
NSSize expressionSize = [self.expressionStorage.rootLayout size];
CGFloat y = (self.bounds.size.height - expressionSize.height) / 2;
NSPoint point = NSMakePoint(self.bounds.origin.x, self.bounds.origin.y + y);
[self.expressionStorage.rootLayout drawAtPoint:point];
// Calculate the position of the expression (probably also forces layout of the expression the first time)
NSPoint expressionOrigin = self.expressionOrigin;
// Draw the selection
if (self.caretVisible || self.selection.length > 0) {
if (self.selection.length == 0) {
[[NSColor blackColor] set];
} else {
[[NSColor selectedTextBackgroundColor] set];
}
NSAffineTransform *transform = [NSAffineTransform transform];
[transform translateXBy:expressionOrigin.x
yBy:expressionOrigin.y];
[transform concat];
NSRectFill([self selectionRect]);
[transform invert];
[transform concat];
}
// Draw the expression
[[NSColor textColor] set];
[self.expressionStorage.rootLayout drawAtPoint:expressionOrigin];
}
@end