210 lines
5.8 KiB
Objective-C
210 lines
5.8 KiB
Objective-C
//
|
|
// MPExpressionLayout.m
|
|
// MathPad
|
|
//
|
|
// Created by Kim Wittenburg on 22.04.14.
|
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
|
//
|
|
|
|
#import "MPExpressionLayout.h"
|
|
#import "MPExpressionStorage.h"
|
|
#import "MPFunctionLayout.h"
|
|
#import "MPModel.h"
|
|
|
|
@implementation MPExpressionLayout
|
|
|
|
#pragma mark Creation Methods
|
|
|
|
- (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
_symbolCache = [[NSMutableArray alloc] init];
|
|
_expressionStorage = expressionStorage;
|
|
_expressionPath = [[NSIndexPath alloc] init];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithExpressionPath:(NSIndexPath *)expressionPath
|
|
parent:(MPFunctionLayout *)parent
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
_symbolCache = [[NSMutableArray alloc] init];
|
|
_expressionPath = expressionPath;
|
|
_parent = parent;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
#pragma mark Properties
|
|
|
|
@synthesize expressionStorage = _expressionStorage;
|
|
- (MPExpressionStorage *)expressionStorage
|
|
{
|
|
if (_expressionStorage) {
|
|
return _expressionStorage;
|
|
}
|
|
return self.parent.expressionStorage;
|
|
}
|
|
|
|
- (MPExpression *)expression
|
|
{
|
|
return [self.expressionStorage symbolAtIndexPath:self.expressionPath];
|
|
}
|
|
|
|
- (NSLayoutManager *)layoutManager
|
|
{
|
|
return self.expressionStorage.layoutManager;
|
|
}
|
|
|
|
- (NSTextContainer *)textContainer
|
|
{
|
|
return self.expressionStorage.textContainer;
|
|
}
|
|
|
|
- (NSTextStorage *)textStorage
|
|
{
|
|
return self.expressionStorage.textStorage;
|
|
}
|
|
|
|
#pragma mark Cache Methods
|
|
|
|
- (void)invalidate
|
|
{
|
|
_valid = NO;
|
|
}
|
|
|
|
- (void)expressionEditedInRange:(NSRange)range replacementLength:(NSUInteger)length
|
|
{
|
|
while (_symbolCache.count < self.expression.numberOfSymbols) {
|
|
[_symbolCache addObject:[NSNull null]];
|
|
}
|
|
NSMutableArray *newPlaceholders = [[NSMutableArray alloc] initWithCapacity:length];
|
|
while (newPlaceholders.count < length) {
|
|
[newPlaceholders addObject:[NSNull null]];
|
|
}
|
|
[_symbolCache replaceObjectsInRange:range withObjectsFromArray:newPlaceholders];
|
|
_valid = NO;
|
|
}
|
|
|
|
- (BOOL)hasCacheForSymbolAtIndex:(NSUInteger)index
|
|
{
|
|
if (index >= _symbolCache.count) {
|
|
return NO;
|
|
}
|
|
return _symbolCache[index] != [NSNull null];
|
|
}
|
|
|
|
- (MPFunctionLayout *)functionLayoutForFunctionAtIndex:(NSUInteger)index;
|
|
{
|
|
if ([self hasCacheForSymbolAtIndex:index]) {
|
|
return _symbolCache[index];
|
|
}
|
|
MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:[self.expressionPath indexPathByAddingIndex:index] parent:self];
|
|
while (index >= _symbolCache.count) {
|
|
[_symbolCache addObject:[NSNull null]];
|
|
}
|
|
_symbolCache[index] = layout;
|
|
return layout;
|
|
}
|
|
|
|
- (NSSize)cachedSizeForSymbolAtIndex:(NSUInteger)index
|
|
{
|
|
id cachedSymbol = _symbolCache[index];
|
|
if ([cachedSymbol isKindOfClass:[NSValue class]]) {
|
|
return [cachedSymbol sizeValue];
|
|
}
|
|
return [(MPFunctionLayout *)cachedSymbol sizeOfFunction];
|
|
}
|
|
|
|
- (void)cacheSize:(NSSize)size forSymbolAtIndex:(NSUInteger)index
|
|
{
|
|
while (index >= _symbolCache.count) {
|
|
[_symbolCache addObject:[NSNull null]];
|
|
}
|
|
_symbolCache[index] = [NSValue valueWithSize:size];
|
|
}
|
|
|
|
#pragma mark Size Calculation Methods
|
|
|
|
- (NSSize)sizeForAllSymbols
|
|
{
|
|
if (!_valid) {
|
|
_cachedSize = [self sizeForSymbolsInRange:NSMakeRange(0, self.expression.numberOfSymbols)];
|
|
_valid = YES;
|
|
}
|
|
return _cachedSize;
|
|
}
|
|
|
|
- (NSSize)sizeForSymbolsInRange:(NSRange)range
|
|
{
|
|
NSSize size = NSMakeSize(0, 0);
|
|
for (NSUInteger index = range.location; index < NSMaxRange(range); index++) {
|
|
NSSize symbolSize = [self sizeForSymbolAtIndex:index];
|
|
size.width += symbolSize.width;
|
|
size.height = MAX(size.height, symbolSize.height);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
- (NSSize)sizeForSymbolAtIndex:(NSUInteger)index
|
|
{
|
|
if ([self hasCacheForSymbolAtIndex:index]) {
|
|
return [self cachedSizeForSymbolAtIndex:index];
|
|
}
|
|
id symbol = [self.expression symbolAtIndex:index];
|
|
if ([symbol isString]) {
|
|
[self.textStorage setString:symbol];
|
|
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
|
|
NSSize symbolSize = [self.layoutManager boundingRectForGlyphRange:glyphRange
|
|
inTextContainer:self.textContainer].size;
|
|
[self cacheSize:symbolSize
|
|
forSymbolAtIndex:index];
|
|
|
|
return symbolSize;
|
|
} else {
|
|
MPFunctionLayout *layout = [self functionLayoutForFunctionAtIndex:index];
|
|
return [layout sizeOfFunction];
|
|
}
|
|
}
|
|
|
|
#pragma mark Drawing Methods
|
|
|
|
- (void)drawSymbolAtIndex:(NSUInteger)index
|
|
atPoint:(NSPoint)point
|
|
{
|
|
id symbol = [self.expression symbolAtIndex:index];
|
|
if ([symbol isString]) {
|
|
[self.textStorage setString:symbol];
|
|
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
|
|
[self.layoutManager drawGlyphsForGlyphRange:glyphRange
|
|
atPoint:point];
|
|
} else {
|
|
MPFunctionLayout *layout = [self functionLayoutForFunctionAtIndex:index];
|
|
[layout drawFunctionAtPoint:point];
|
|
}
|
|
}
|
|
|
|
- (void)drawSymbolsInRange:(NSRange)range
|
|
atPoint:(NSPoint)point
|
|
{
|
|
NSSize overallSize = [self sizeForSymbolsInRange:range];
|
|
CGFloat x = point.x;
|
|
for (NSUInteger index = range.location; index < NSMaxRange(range); index++) {
|
|
NSSize symbolSize = [self sizeForSymbolAtIndex:index];
|
|
CGFloat dy = (overallSize.height - symbolSize.height) / 2;
|
|
[self drawSymbolAtIndex:index
|
|
atPoint:NSMakePoint(x, point.y + dy)];
|
|
x += symbolSize.width;
|
|
}
|
|
}
|
|
|
|
- (void)drawAllSymbolsAtPoint:(NSPoint)point
|
|
{
|
|
[self drawSymbolsInRange:NSMakeRange(0, [self.expression numberOfSymbols]) atPoint:point];
|
|
}
|
|
|
|
@end
|