Archived
1

Added MPExpressionLayout, MPFunctionLayout and MPExpressionStorage as Rendering System for Expressions

This commit is contained in:
Kim Wittenburg
2014-04-23 03:13:55 +02:00
parent 8605a6ac7b
commit 3116925315
8 changed files with 597 additions and 6 deletions

View File

@@ -0,0 +1,63 @@
//
// MPExpressionLayout.h
// MathPad
//
// Created by Kim Wittenburg on 22.04.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
@class MPExpressionLayout, MPFunctionLayout, MPExpressionStorage, MPExpression;
@interface MPExpressionLayout : NSObject {
BOOL _valid;
NSSize _cachedSize;
NSMutableArray *_symbolCache;
}
#pragma mark Creation Methods
// -init not supported
- (id)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage;
- (id)initWithExpressionPath:(NSIndexPath *)expressionPath
parent:(MPFunctionLayout *)parent;
#pragma mark Properties
@property (readonly, nonatomic, weak) MPFunctionLayout *parent;
@property (readonly, nonatomic, weak) MPExpressionStorage *expressionStorage;
@property (readonly, nonatomic, strong) NSIndexPath *expressionPath;
- (MPExpression *)expression; // Convenience
- (NSLayoutManager *)layoutManager;
- (NSTextContainer *)textContainer;
- (NSTextStorage *)textStorage;
#pragma mark Cache Methods
- (void)invalidate;
- (void)expressionEditedInRange:(NSRange)range
replacementLength:(NSUInteger)length;
- (BOOL)hasCacheForSymbolAtIndex:(NSUInteger)index;
- (MPFunctionLayout *)functionLayoutForFunctionAtIndex:(NSUInteger)index;
- (NSSize)cachedSizeForSymbolAtIndex:(NSUInteger)index;
- (void)cacheSize:(NSSize)size forSymbolAtIndex:(NSUInteger)index;
#pragma mark Sizes Calculation Methods
- (NSSize)sizeForAllSymbols;
- (NSSize)sizeForSymbolAtIndex:(NSUInteger)index;
- (NSSize)sizeForSymbolsInRange:(NSRange)range;
#pragma mark Drawing Methods
- (void)drawSymbolAtIndex:(NSUInteger)index
atPoint:(NSPoint)point;
- (void)drawSymbolsInRange:(NSRange)range
atPoint:(NSPoint)point;
- (void)drawAllSymbolsAtPoint:(NSPoint)point;
@end

View File

@@ -0,0 +1,209 @@
//
// 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

View File

@@ -0,0 +1,21 @@
//
// MPExpressionStorage.h
// MathPad
//
// Created by Kim Wittenburg on 22.04.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPExpression.h"
@class MPExpressionStorage, MPExpressionLayout;
@interface MPExpressionStorage : MPMutableExpression
@property (nonatomic, strong) MPExpressionLayout *expressionLayout;
- (NSLayoutManager *)layoutManager;
- (NSTextContainer *)textContainer;
- (NSTextStorage *)textStorage;
@end

View File

@@ -0,0 +1,60 @@
//
// MPExpressionStorage.m
// MathPad
//
// Created by Kim Wittenburg on 22.04.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPExpressionStorage.h"
#import "MPExpressionLayout.h"
@interface MPExpressionStorage ()
@property (nonatomic, strong) NSLayoutManager *layoutManager;
@property (nonatomic, strong) NSTextContainer *textContainer;
@property (nonatomic, strong) NSTextStorage *textStorage;
@end
@implementation MPExpressionStorage
- (instancetype)initWithSymbols:(NSArray *)symbols
{
self = [super initWithSymbols:symbols];
if (self) {
_expressionLayout = [[MPExpressionLayout alloc] initRootLayoutWithExpressionStorage:self];
}
return self;
}
- (NSLayoutManager *)layoutManager
{
[self ensureTextSystemObjects];
return _layoutManager;
}
- (NSTextContainer *)textContainer
{
[self ensureTextSystemObjects];
return _textContainer;
}
- (NSTextStorage *)textStorage
{
[self ensureTextSystemObjects];
return _textStorage;
}
- (void)ensureTextSystemObjects
{
if (_layoutManager == nil || _textContainer == nil || _textStorage == nil) {
_textStorage = [[NSTextStorage alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];
[_textStorage addLayoutManager:layoutManager];
_textContainer = textContainer;
_layoutManager = layoutManager;
}
}
@end

View File

@@ -6,13 +6,24 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
// TODO: Undo/Redo + Delegate
#import <Cocoa/Cocoa.h>
@class MPExpressionView, MPRange;
@class MPExpressionView, MPExpressionStorage, MPExpressionLayout, MPRangePath;
@interface MPExpressionView : NSView
@interface MPExpressionView : NSView <NSUserInterfaceValidations>
#pragma mark Creation Methods
- (id)initWithFrame:(NSRect)frameRect;
#pragma mark Properties
@property (readonly, nonatomic, copy) MPExpressionStorage *expressionStorage;
- (MPExpressionLayout *)expressionLayout; // Convenience Method
@property (nonatomic, getter = isEditable) BOOL editable;
@property (nonatomic, strong) MPRange *selection;
@property (nonatomic, strong) MPRangePath *selection;
@end

View File

@@ -7,23 +7,73 @@
//
#import "MPExpressionView.h"
#import "MPExpressionLayout.h"
#import "MPExpressionStorage.h"
#import "NSObject+MPStringTest.h"
#import "MPSumFunction.h"
@interface MPExpressionView ()
@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) {
// Initialization code here.
[self initializeObjects];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self initializeObjects];
}
return self;
}
- (void)initializeObjects
{
MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithSymbols:@[@"12345", [[MPSumFunction alloc] init]]];
_expressionStorage = expressionStorage;
}
#pragma mark Properties
- (MPExpressionLayout *)expressionLayout
{
return self.expressionStorage.expressionLayout;
}
#pragma mark Drawing Methods
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
[[NSColor whiteColor] set];
NSRectFill(self.bounds);
[[NSColor blackColor] set];
NSSize expressionSize = [self.expressionLayout sizeForAllSymbols];
CGFloat y = (self.bounds.size.height - expressionSize.height) / 2;
NSLog(@"%f", self.bounds.origin.y);
NSPoint point = NSMakePoint(self.bounds.origin.x, self.bounds.origin.y + y);
[self.expressionLayout drawAllSymbolsAtPoint:point];
}
@end

View File

@@ -0,0 +1,56 @@
//
// MPFunctionLayout.h
// MathPad
//
// Created by Kim Wittenburg on 22.04.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
@class MPFunctionLayout, MPExpressionLayout, MPExpressionStorage, MPFunction;
@interface MPFunctionLayout : NSObject {
@protected
BOOL _valid;
NSSize _cachedSize;
NSMutableArray *_childCache;
}
#pragma mark Creation Methods
+ (instancetype)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)functionPath
parent:(MPExpressionLayout *)parent;
- (id)initWithFunctionPath:(NSIndexPath *)functionPath
parent:(MPExpressionLayout *)parent;
#pragma mark Properties
@property (readonly, nonatomic, weak) MPExpressionLayout *parent;
@property (readonly, nonatomic, strong) NSIndexPath *functionPath;
- (MPExpressionStorage *)expressionStorage;
- (MPFunction *)function; // Convenience
- (NSLayoutManager *)layoutManager;
- (NSTextContainer *)textContainer;
- (NSTextStorage *)textStorage;
#pragma mark Cache Methods
- (void)invalidate;
- (BOOL)hasCacheForChildAtIndex:(NSUInteger)index;
- (MPExpressionLayout *)expressionLayoutForChildAtIndex:(NSUInteger)index;
#pragma mark Size Calculation Methods
- (NSSize)sizeOfFunction;
- (NSSize)calculateSize;
#pragma mark Drawing Methods
- (void)drawFunctionAtPoint:(NSPoint)point;
@end

121
MathPad/MPFunctionLayout.m Normal file
View File

@@ -0,0 +1,121 @@
//
// MPFunctionLayout.m
// MathPad
//
// Created by Kim Wittenburg on 22.04.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPFunctionLayout.h"
#import "MPExpressionLayout.h"
#import "MPExpressionStorage.h"
#import "MPFunction.h"
#import "MPSumFunction.h"
#import "MPSumFunctionLayout.h"
@implementation MPFunctionLayout
#pragma mark Creation Methods
+ (instancetype)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)functionPath
parent:(MPExpressionLayout *)parent
{
MPFunction *function = [parent.expressionStorage symbolAtIndexPath:functionPath];
Class class = [function class];
if (class == [MPSumFunction class]) {
return [[MPSumFunctionLayout alloc] initWithFunctionPath:functionPath parent:parent];
}
return nil;
}
- (id)initWithFunctionPath:(NSIndexPath *)functionPath
parent:(MPExpressionLayout *)parent
{
self = [super init];
if (self) {
_functionPath = functionPath;
_parent = parent;
_childCache = [[NSMutableArray alloc] init];
}
return self;
}
#pragma mark Properties
- (MPExpressionStorage *)expressionStorage
{
return self.parent.expressionStorage;
}
- (MPFunction *)function
{
return [self.expressionStorage symbolAtIndexPath:self.functionPath];
}
- (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;
}
- (BOOL)hasCacheForChildAtIndex:(NSUInteger)index
{
if (index >= _childCache.count) {
return NO;
}
return _childCache[index] != [NSNull null];
}
- (MPExpressionLayout *)expressionLayoutForChildAtIndex:(NSUInteger)index
{
if ([self hasCacheForChildAtIndex:index]) {
return _childCache[index];
}
while (index >= _childCache.count) {
[_childCache addObject:[NSNull null]];
}
MPExpressionLayout *expressionLayout = [[MPExpressionLayout alloc] initWithExpressionPath:[self.functionPath indexPathByAddingIndex:index] parent:self];
_childCache[index] = expressionLayout;
return expressionLayout;
}
#pragma mark Size Calculation Methods
- (NSSize)sizeOfFunction
{
if (!_valid) {
_cachedSize = [self calculateSize];
_valid = YES;
}
return _cachedSize;
}
- (NSSize)calculateSize
{
return NSMakeSize(0, 0);
}
#pragma mark Drawing Methods
- (void)drawFunctionAtPoint:(NSPoint)point
{
}
@end