171 lines
5.0 KiB
Objective-C
171 lines
5.0 KiB
Objective-C
//
|
|
// MPExpressionLayout.m
|
|
// MathPad
|
|
//
|
|
// Created by Kim Wittenburg on 07.08.14.
|
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
|
//
|
|
|
|
#import "MPExpressionLayout.h"
|
|
#import "MPFunctionLayout.h"
|
|
|
|
@interface MPExpressionLayout (MPLineGeneration)
|
|
|
|
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index;
|
|
- (CTLineRef)createLineForString:(NSString *)aString;
|
|
|
|
@end
|
|
|
|
@implementation MPExpressionLayout (MPLineGeneration)
|
|
|
|
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index
|
|
{
|
|
id element = [self.expression elementAtIndex:index];
|
|
if (![element isString]) {
|
|
return NULL;
|
|
}
|
|
NSString *string = element;
|
|
id lineObject = [self cachableObjectForIndex:index generator:^id{
|
|
CTLineRef line = [self createLineForString:string];
|
|
return CFBridgingRelease(line);
|
|
}];
|
|
return (__bridge CTLineRef)lineObject;
|
|
}
|
|
|
|
- (CTLineRef)createLineForString:(NSString *)aString
|
|
{
|
|
|
|
NSAttributedString *text = [[NSAttributedString alloc] initWithString:aString
|
|
attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}];
|
|
CFAttributedStringRef attributedString = CFBridgingRetain(text);
|
|
CTLineRef line = CTLineCreateWithAttributedString(attributedString);
|
|
CFRelease(attributedString); // TODO: Is this release appropriate?
|
|
return line;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation MPExpressionLayout
|
|
|
|
# pragma mark Creation Methods
|
|
- (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
_expressionStorage = expressionStorage;
|
|
_expression = expressionStorage;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithPath:(NSIndexPath *)path
|
|
parent:(MPLayout *)parent
|
|
{
|
|
self = [super initWithPath:path
|
|
parent:parent];
|
|
if (self) {
|
|
_expression = [parent.expressionStorage elementAtIndexPath:path];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
#pragma mark Properties
|
|
@synthesize expressionStorage = _expressionStorage;
|
|
- (MPExpressionStorage *)expressionStorage
|
|
{
|
|
if (_expressionStorage) {
|
|
return _expressionStorage;
|
|
}
|
|
return self.parent.expressionStorage;
|
|
}
|
|
|
|
//- (MPExpression *)expression
|
|
//{
|
|
// return [self.expressionStorage elementAtIndexPath:self.path];
|
|
//}
|
|
|
|
#pragma mark Cache Methods
|
|
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
|
{
|
|
id cachedObject = [self cachableObjectForIndex:index generator:^id{
|
|
NSIndexPath *indexPath = [self.expression.indexPath indexPathByAddingIndex:index];
|
|
MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:indexPath
|
|
parent:self];
|
|
return layout;
|
|
}];
|
|
if ([cachedObject isKindOfClass:[MPLayout class]]) {
|
|
return cachedObject;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (NSSize)sizeForElementAtIndex:(NSUInteger)index
|
|
{
|
|
id symbol = [self.expression elementAtIndex:index];
|
|
if ([symbol isString]) {
|
|
CTLineRef line = [self lineForElementAtIndex:index];
|
|
CFRetain(line);
|
|
CGRect bounds = CTLineGetBoundsWithOptions(line, /*kCTLineBoundsUseOpticalBounds*/ 0);
|
|
CFRelease(line);
|
|
return bounds.size;
|
|
} else {
|
|
return [self childLayoutAtIndex:index].size;
|
|
}
|
|
}
|
|
|
|
#pragma mark Drawing Methods
|
|
- (NSSize)generateSize
|
|
{
|
|
CGFloat width = 0, height = 0;
|
|
for (NSUInteger index = 0; index < self.expression.numberOfElements; index++) {
|
|
NSSize elementSize = [self sizeForElementAtIndex:index];
|
|
width += elementSize.width;
|
|
height = MAX(height, elementSize.height);
|
|
}
|
|
return NSMakeSize(width, height);
|
|
}
|
|
|
|
- (void)drawAtPoint:(NSPoint)point
|
|
{
|
|
// Get the current context
|
|
CGContextRef context =
|
|
(CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
|
CGContextSaveGState(context);
|
|
|
|
// Set the text matrix
|
|
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
|
|
|
|
// Track the x position
|
|
CGFloat x = point.x;
|
|
|
|
for (NSUInteger index = 0; index < self.expression.numberOfElements; index++) {
|
|
// The current element
|
|
id element = [self.expression elementAtIndex:index];
|
|
NSSize elementSize = [self sizeForElementAtIndex:index];
|
|
CGFloat dy = (self.size.height - elementSize.height) / 2;
|
|
CGFloat y = point.y + dy;
|
|
|
|
if ([element isString]) {
|
|
// Get the line to draw
|
|
CTLineRef line = [self lineForElementAtIndex:index];
|
|
CFRetain(line);
|
|
|
|
// Move to the appropriate position
|
|
CGContextSetTextPosition(context, x, y);
|
|
|
|
// Perform the drawing
|
|
CTLineDraw(line, context);
|
|
|
|
CFRelease(line);
|
|
} else {
|
|
// Let the child layout draw itself
|
|
MPLayout *layout = [self childLayoutAtIndex:index];
|
|
[layout drawAtPoint:NSMakePoint(x, y)];
|
|
}
|
|
x += elementSize.width;
|
|
}
|
|
CGContextRestoreGState(context);
|
|
}
|
|
|
|
@end
|