Fundamental Redesign of the View and Controller
This commit is contained in:
@@ -61,7 +61,6 @@
|
|||||||
#pragma mark Actions
|
#pragma mark Actions
|
||||||
- (IBAction)changeExpression:(id)sender {
|
- (IBAction)changeExpression:(id)sender {
|
||||||
[self.resultExpressionView.expressionStorage insertElement:@"abc" atLocation:6];
|
[self.resultExpressionView.expressionStorage insertElement:@"abc" atLocation:6];
|
||||||
self.resultExpressionView.needsDisplay = YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -14,6 +14,6 @@
|
|||||||
|
|
||||||
- (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage;
|
- (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage;
|
||||||
|
|
||||||
@property (readonly, nonatomic, weak) MPExpression *expression; // Convenience
|
@property (readonly, nonatomic, weak) MPExpression *expression;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -9,45 +9,38 @@
|
|||||||
#import "MPExpressionLayout.h"
|
#import "MPExpressionLayout.h"
|
||||||
#import "MPFunctionLayout.h"
|
#import "MPFunctionLayout.h"
|
||||||
|
|
||||||
@interface MPExpressionLayout (MPPathGeneration)
|
@interface MPExpressionLayout (MPLineGeneration)
|
||||||
|
|
||||||
- (NSBezierPath *)bezierPathForChildAtIndex:(NSUInteger)index;
|
|
||||||
- (NSBezierPath *)generateBezierPathForString:(NSString *)aString;
|
|
||||||
|
|
||||||
|
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index;
|
||||||
|
- (CTLineRef)createLineForString:(NSString *)aString;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MPExpressionLayout (MPPathGeneration)
|
@implementation MPExpressionLayout (MPLineGeneration)
|
||||||
|
|
||||||
- (NSBezierPath *)bezierPathForChildAtIndex:(NSUInteger)index
|
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
id symbol = [self.expression elementAtIndex:index];
|
id element = [self.expression elementAtIndex:index];
|
||||||
if ([symbol isString]) {
|
if (![element isString]) {
|
||||||
return [self cachableObjectForIndex:index
|
return NULL;
|
||||||
generator:^id{
|
|
||||||
return [self generateBezierPathForString:symbol];
|
|
||||||
}];
|
|
||||||
} else {
|
|
||||||
MPLayout *layout = [self childLayoutAtIndex:index];
|
|
||||||
return layout.bezierPath;
|
|
||||||
}
|
}
|
||||||
|
NSString *string = element;
|
||||||
|
id lineObject = [self cachableObjectForIndex:index generator:^id{
|
||||||
|
CTLineRef line = [self createLineForString:string];
|
||||||
|
return CFBridgingRelease(line);
|
||||||
|
}];
|
||||||
|
return (__bridge CTLineRef)lineObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSBezierPath *)generateBezierPathForString:(NSString *)aString
|
- (CTLineRef)createLineForString:(NSString *)aString
|
||||||
{
|
{
|
||||||
|
|
||||||
NSAttributedString *text = [[NSAttributedString alloc] initWithString:aString
|
NSAttributedString *text = [[NSAttributedString alloc] initWithString:aString
|
||||||
attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}];
|
attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}];
|
||||||
self.textStorage.attributedString = text;
|
CFAttributedStringRef attributedString = CFBridgingRetain(text);
|
||||||
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
|
CTLineRef line = CTLineCreateWithAttributedString(attributedString);
|
||||||
NSGlyph glyphs[glyphRange.length+1];
|
CFRelease(attributedString); // TODO: Is this release appropriate?
|
||||||
NSUInteger actualGlyphCount = [self.layoutManager getGlyphs:glyphs
|
return line;
|
||||||
range:glyphRange];
|
|
||||||
NSBezierPath *path = [NSBezierPath bezierPath];
|
|
||||||
[path moveToPoint:NSZeroPoint];
|
|
||||||
[path appendBezierPathWithGlyphs:glyphs
|
|
||||||
count:actualGlyphCount
|
|
||||||
inFont:[NSFont fontWithName:@"Lucida Grande" size:18.0]];
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -60,6 +53,18 @@
|
|||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
_expressionStorage = expressionStorage;
|
_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;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -74,52 +79,92 @@
|
|||||||
return self.parent.expressionStorage;
|
return self.parent.expressionStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPExpression *)expression
|
//- (MPExpression *)expression
|
||||||
{
|
//{
|
||||||
return [self.expressionStorage elementAtIndexPath:self.path];
|
// return [self.expressionStorage elementAtIndexPath:self.path];
|
||||||
}
|
//}
|
||||||
|
|
||||||
#pragma mark Cache Methods
|
#pragma mark Cache Methods
|
||||||
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
id cachedObject = [self cachableObjectForIndex:index generator:^id{
|
id cachedObject = [self cachableObjectForIndex:index generator:^id{
|
||||||
NSIndexPath *indexPath = [self.path indexPathByAddingIndex:index];
|
NSIndexPath *indexPath = [self.expression.indexPath indexPathByAddingIndex:index];
|
||||||
MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:indexPath
|
MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:indexPath
|
||||||
parent:self];
|
parent:self];
|
||||||
return layout;
|
return layout;
|
||||||
}];
|
}];
|
||||||
if ([cachedObject isKindOfClass:[NSBezierPath class]]) {
|
if ([cachedObject isKindOfClass:[MPLayout class]]) {
|
||||||
return nil;
|
return cachedObject;
|
||||||
}
|
}
|
||||||
return cachedObject;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSSize)sizeForChildAtIndex:(NSUInteger)index
|
- (NSSize)sizeForElementAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
id symbol = [self.expression elementAtIndex:index];
|
id symbol = [self.expression elementAtIndex:index];
|
||||||
if ([symbol isString]) {
|
if ([symbol isString]) {
|
||||||
return [self bezierPathForChildAtIndex:index].bounds.size;
|
CTLineRef line = [self lineForElementAtIndex:index];
|
||||||
|
CFRetain(line);
|
||||||
|
CGRect bounds = CTLineGetBoundsWithOptions(line, /*kCTLineBoundsUseOpticalBounds*/ 0);
|
||||||
|
CFRelease(line);
|
||||||
|
return bounds.size;
|
||||||
} else {
|
} else {
|
||||||
return [self childLayoutAtIndex:index].size;
|
return [self childLayoutAtIndex:index].size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Drawing Methods
|
#pragma mark Drawing Methods
|
||||||
- (NSBezierPath *)generateBezierPath
|
- (NSSize)generateSize
|
||||||
{
|
{
|
||||||
NSBezierPath *fullPath = [NSBezierPath bezierPath];
|
CGFloat width = 0, height = 0;
|
||||||
[fullPath moveToPoint:NSZeroPoint];
|
for (NSUInteger index = 0; index < self.expression.numberOfElements; index++) {
|
||||||
NSUInteger x = 0;
|
NSSize elementSize = [self sizeForElementAtIndex:index];
|
||||||
for (NSInteger index = 0; index < self.expression.numberOfElements; ++index) {
|
width += elementSize.width;
|
||||||
NSAffineTransform *transform = [NSAffineTransform transform];
|
height = MAX(height, elementSize.height);
|
||||||
// TODO: Translate by the right amount
|
|
||||||
[transform translateXBy:x yBy:0];
|
|
||||||
NSBezierPath *path = [self bezierPathForChildAtIndex:index].copy;
|
|
||||||
[path transformUsingAffineTransform:transform];
|
|
||||||
[fullPath appendBezierPath:path];
|
|
||||||
x += path.bounds.size.width;
|
|
||||||
}
|
}
|
||||||
return fullPath;
|
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
|
@end
|
||||||
|
|||||||
@@ -8,10 +8,14 @@
|
|||||||
|
|
||||||
#import "MPExpression.h"
|
#import "MPExpression.h"
|
||||||
|
|
||||||
@class MPExpressionStorage, MPExpressionLayout;
|
@class MPExpressionStorage, MPExpressionView, MPExpressionLayout;
|
||||||
|
|
||||||
@interface MPExpressionStorage : MPExpression
|
@interface MPExpressionStorage : MPExpression
|
||||||
|
|
||||||
|
- (instancetype)initWithExpressionView:(MPExpressionView *)expressionView
|
||||||
|
elements:(NSArray *)elements;
|
||||||
|
|
||||||
|
@property (readonly, nonatomic, weak) MPExpressionView *expressionView;
|
||||||
@property (nonatomic, strong) MPExpressionLayout *rootLayout;
|
@property (nonatomic, strong) MPExpressionLayout *rootLayout;
|
||||||
|
|
||||||
- (NSLayoutManager *)layoutManager;
|
- (NSLayoutManager *)layoutManager;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "MPExpressionStorage.h"
|
#import "MPExpressionStorage.h"
|
||||||
|
#import "MPExpressionView.h"
|
||||||
#import "MPExpressionLayout.h"
|
#import "MPExpressionLayout.h"
|
||||||
#import "MPFunctionLayout.h"
|
#import "MPFunctionLayout.h"
|
||||||
#import "MPRangePath.h"
|
#import "MPRangePath.h"
|
||||||
@@ -19,6 +20,15 @@
|
|||||||
|
|
||||||
@implementation MPExpressionStorage
|
@implementation MPExpressionStorage
|
||||||
|
|
||||||
|
- (instancetype)initWithExpressionView:(MPExpressionView *)expressionView elements:(NSArray *)elements
|
||||||
|
{
|
||||||
|
self = [self initWithElements:elements];
|
||||||
|
if (self) {
|
||||||
|
_expressionView = expressionView;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
- (instancetype)initWithElements:(NSArray *)elements
|
- (instancetype)initWithElements:(NSArray *)elements
|
||||||
{
|
{
|
||||||
self = [super initWithElements:elements];
|
self = [super initWithElements:elements];
|
||||||
@@ -69,7 +79,9 @@
|
|||||||
for (NSUInteger index = 1; index < rangePath.location.length-1; index++) {
|
for (NSUInteger index = 1; index < rangePath.location.length-1; index++) {
|
||||||
current = [current childLayoutAtIndex:index];
|
current = [current childLayoutAtIndex:index];
|
||||||
}
|
}
|
||||||
[current clearCacheInRange:rangePath.rangeAtLastIndex replacementLength:replacementLength];
|
[current clearCacheInRange:rangePath.rangeAtLastIndex
|
||||||
|
replacementLength:replacementLength];
|
||||||
|
self.expressionView.needsDisplay = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
@class MPExpressionView, MPExpressionStorage, MPExpressionLayout, MPRangePath;
|
@class MPExpressionView, MPExpressionStorage, MPExpressionLayout, MPRangePath;
|
||||||
|
|
||||||
@interface MPExpressionView : NSView <NSUserInterfaceValidations>
|
@interface MPExpressionView : NSView
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
|
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
@property (readonly, nonatomic, strong) MPExpressionStorage *expressionStorage;
|
@property (readonly, nonatomic, strong) MPExpressionStorage *expressionStorage;
|
||||||
|
|
||||||
@property (nonatomic, getter = isEditable) BOOL editable;
|
@property (nonatomic, getter = isEditable) BOOL editable;
|
||||||
@property (nonatomic, strong) MPRangePath *selection;
|
//@property (nonatomic, strong) MPRangePath *selection;
|
||||||
|
@property (nonatomic, strong) NSIndexPath *caretLocation;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#import "MPSumFunction.h"
|
#import "MPSumFunction.h"
|
||||||
|
|
||||||
@interface MPExpressionView (MPCursor)
|
@interface MPExpressionView (MPCursor)
|
||||||
@property (nonatomic, strong) NSTimer *cursorTimer;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MPExpressionView
|
@implementation MPExpressionView
|
||||||
@@ -49,8 +49,9 @@
|
|||||||
|
|
||||||
- (void)initializeObjects
|
- (void)initializeObjects
|
||||||
{
|
{
|
||||||
MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithElements:@[@"12345", [[MPSumFunction alloc] init], [[MPSumFunction alloc] init]]];
|
MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithExpressionView:self elements:@[@"12345", [[MPSumFunction alloc] init], [[MPSumFunction alloc] init]]];
|
||||||
_expressionStorage = expressionStorage;
|
_expressionStorage = expressionStorage;
|
||||||
|
_caretLocation = [[NSIndexPath alloc] initWithIndex:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Properties
|
#pragma mark Properties
|
||||||
@@ -60,9 +61,25 @@
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
|
- (BOOL)acceptsFirstResponder
|
||||||
{
|
{
|
||||||
_expressionStorage = expressionStorage;
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)canBecomeKeyView
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark Event Handling
|
||||||
|
- (void)keyDown:(NSEvent *)theEvent
|
||||||
|
{
|
||||||
|
[self interpretKeyEvents:@[theEvent]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)moveRight:(id)sender
|
||||||
|
{
|
||||||
|
[self.expressionStorage deleteElementsInRange:NSMakeRange(0, 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Drawing Methods
|
#pragma mark Drawing Methods
|
||||||
|
|||||||
@@ -14,18 +14,24 @@
|
|||||||
+ (MPFunctionLayout *)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)path
|
+ (MPFunctionLayout *)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)path
|
||||||
parent:(MPExpressionLayout *)parent;
|
parent:(MPExpressionLayout *)parent;
|
||||||
|
|
||||||
@property (readonly, nonatomic, weak) MPFunction *function; // Convenience
|
@property (readonly, nonatomic, weak) MPFunction *function;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MPFunctionLayout (MPSubclassOverride)
|
@interface MPFunctionLayout (MPSubclassOverride)
|
||||||
|
|
||||||
|
#pragma mark Cache Methods
|
||||||
|
- (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index
|
||||||
|
generator:(CTLineRef (^)())generator;
|
||||||
|
|
||||||
// Should also implement accessor method for special function type:
|
// Should also implement accessor method for special function type:
|
||||||
// - (MPCustomFunction *)customFunction
|
// - (MPCustomFunction *)customFunction
|
||||||
// {
|
// {
|
||||||
// return (MPCustomFunction *)self.function;
|
// return (MPCustomFunction *)self.function;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
- (NSBezierPath *)generateBezierPath;
|
#pragma mark Size and Drawing Methods
|
||||||
|
- (NSSize)generateSize; // To be implemented
|
||||||
|
- (void)drawAtPoint:(NSPoint)point; // To be implemented
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -27,32 +27,63 @@
|
|||||||
return [[self alloc] initWithPath:path parent:parent];
|
return [[self alloc] initWithPath:path parent:parent];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Properties
|
- (instancetype)initWithPath:(NSIndexPath *)path
|
||||||
- (MPFunction *)function
|
parent:(MPLayout *)parent
|
||||||
{
|
{
|
||||||
return [self.expressionStorage elementAtIndexPath:self.path];
|
self = [super initWithPath:path
|
||||||
|
parent:parent];
|
||||||
|
if (self) {
|
||||||
|
_function = [parent.expressionStorage elementAtIndexPath:path];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark Properties
|
||||||
|
//- (MPFunction *)function
|
||||||
|
//{
|
||||||
|
// return [self.expressionStorage elementAtIndexPath:self.path];
|
||||||
|
//}
|
||||||
|
|
||||||
#pragma mark Cache Methods
|
#pragma mark Cache Methods
|
||||||
|
- (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index
|
||||||
|
generator:(CTLineRef (^)())generator
|
||||||
|
{
|
||||||
|
NSUInteger actualIndex = self.function.numberOfChildren + index;
|
||||||
|
id (^actualGenerator)() = ^{
|
||||||
|
CTLineRef line = generator();
|
||||||
|
return CFBridgingRelease(line);
|
||||||
|
};
|
||||||
|
id lineObject = [self cachableObjectForIndex:actualIndex
|
||||||
|
generator:actualGenerator];
|
||||||
|
return (__bridge CTLineRef)(lineObject);
|
||||||
|
}
|
||||||
|
|
||||||
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return [self cachableObjectForIndex:index generator:^id{
|
return [self cachableObjectForIndex:index generator:^id{
|
||||||
NSIndexPath *childPath = [self.path indexPathByAddingIndex:index];
|
NSIndexPath *childPath = [self.function.indexPath indexPathByAddingIndex:index];
|
||||||
MPExpressionLayout *layout = [[MPExpressionLayout alloc] initWithPath:childPath
|
MPExpressionLayout *layout = [[MPExpressionLayout alloc] initWithPath:childPath
|
||||||
parent:self];
|
parent:self];
|
||||||
return layout;
|
return layout;
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark Size and Drawing Methods
|
||||||
|
|
||||||
- (NSSize)sizeForChildAtIndex:(NSUInteger)index
|
- (NSSize)sizeForChildAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
MPLayout *childLayout = [self childLayoutAtIndex:index];
|
MPLayout *childLayout = [self childLayoutAtIndex:index];
|
||||||
return [childLayout size];
|
return [childLayout size];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSBezierPath *)generateBezierPath
|
- (NSSize)generateSize
|
||||||
{
|
{
|
||||||
return [NSBezierPath bezierPath];
|
return NSZeroSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)drawAtPoint:(NSPoint)point
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -20,13 +20,9 @@
|
|||||||
|
|
||||||
#pragma mark Text System Objects
|
#pragma mark Text System Objects
|
||||||
@property (readonly, nonatomic, weak) MPExpressionStorage *expressionStorage;
|
@property (readonly, nonatomic, weak) MPExpressionStorage *expressionStorage;
|
||||||
@property (readonly, nonatomic, weak) NSLayoutManager *layoutManager;
|
|
||||||
@property (readonly, nonatomic, weak) NSTextContainer *textContainer;
|
|
||||||
@property (readonly, nonatomic, weak) NSTextStorage *textStorage;
|
|
||||||
|
|
||||||
#pragma mark Cache Tree
|
#pragma mark Cache Tree
|
||||||
@property (readonly, nonatomic, weak) MPLayout *parent;
|
@property (readonly, nonatomic, weak) MPLayout *parent;
|
||||||
@property (readonly, nonatomic, strong) NSIndexPath *path;
|
|
||||||
|
|
||||||
#pragma mark Cache Methods
|
#pragma mark Cache Methods
|
||||||
// Querying Caches
|
// Querying Caches
|
||||||
@@ -39,18 +35,14 @@
|
|||||||
- (void)invalidate;
|
- (void)invalidate;
|
||||||
|
|
||||||
#pragma mark Calculation and Drawing Methods
|
#pragma mark Calculation and Drawing Methods
|
||||||
|
// TODO: Implement Small Size
|
||||||
// @property (nonatomic) BOOL usesSmallSize;
|
// @property (nonatomic) BOOL usesSmallSize;
|
||||||
- (NSSize)size;
|
- (NSSize)size;
|
||||||
|
|
||||||
- (NSBezierPath *)bezierPath;
|
|
||||||
- (NSBezierPath *)bezierPathAtOrigin:(NSPoint)point;
|
|
||||||
|
|
||||||
- (void)drawAtPoint:(NSPoint)point;
|
- (void)drawAtPoint:(NSPoint)point;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MPLayout (MPSubclassImplement)
|
@interface MPLayout (MPSubclassImplement)
|
||||||
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index; // To be implemented
|
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index; // To be implemented
|
||||||
- (NSSize)sizeForChildAtIndex:(NSUInteger)index; // To be implemented
|
- (NSSize)generateSize; // To be implemented
|
||||||
- (NSBezierPath *)generateBezierPath; // To be implemented
|
|
||||||
@end
|
@end
|
||||||
@@ -10,19 +10,15 @@
|
|||||||
|
|
||||||
@interface MPLayout ()
|
@interface MPLayout ()
|
||||||
|
|
||||||
// Querying Caches
|
// Querying and Storing Caches
|
||||||
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index;
|
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index;
|
||||||
|
|
||||||
// Storing Caches
|
|
||||||
- (void)cacheObject:(id)anObject
|
|
||||||
forElementAtIndex:(NSUInteger)index;
|
|
||||||
- (void)ensureCacheSizeForIndex:(NSUInteger)index;
|
- (void)ensureCacheSizeForIndex:(NSUInteger)index;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MPLayout {
|
@implementation MPLayout {
|
||||||
NSMutableArray *_cache;
|
NSMutableArray *_cache;
|
||||||
NSBezierPath *_cachedPath;
|
NSSize _cachedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
@@ -31,8 +27,7 @@
|
|||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
_cache = [[NSMutableArray alloc] init];
|
_cache = [[NSMutableArray alloc] init];
|
||||||
_cachedPath = nil;
|
_cachedSize = NSZeroSize;
|
||||||
_path = [[NSIndexPath alloc] init];
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -42,7 +37,6 @@
|
|||||||
{
|
{
|
||||||
self = [self init];
|
self = [self init];
|
||||||
if (self) {
|
if (self) {
|
||||||
_path = path;
|
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -54,23 +48,8 @@
|
|||||||
return self.parent.expressionStorage;
|
return self.parent.expressionStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSLayoutManager *)layoutManager
|
|
||||||
{
|
|
||||||
return self.expressionStorage.layoutManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSTextContainer *)textContainer
|
|
||||||
{
|
|
||||||
return self.expressionStorage.textContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSTextStorage *)textStorage
|
|
||||||
{
|
|
||||||
return self.expressionStorage.textStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Cache Tree
|
#pragma mark Cache Tree
|
||||||
// Querying Caches
|
// Querying and Storing Caches
|
||||||
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index
|
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
if (index >= _cache.count) {
|
if (index >= _cache.count) {
|
||||||
@@ -86,17 +65,9 @@
|
|||||||
return _cache[index];
|
return _cache[index];
|
||||||
}
|
}
|
||||||
id object = generator();
|
id object = generator();
|
||||||
[self cacheObject:object
|
|
||||||
forElementAtIndex:index];
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Storing Caches
|
|
||||||
- (void)cacheObject:(id)anObject
|
|
||||||
forElementAtIndex:(NSUInteger)index
|
|
||||||
{
|
|
||||||
[self ensureCacheSizeForIndex:index];
|
[self ensureCacheSizeForIndex:index];
|
||||||
_cache[index] = anObject;
|
_cache[index] = object;
|
||||||
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)ensureCacheSizeForIndex:(NSUInteger)index
|
- (void)ensureCacheSizeForIndex:(NSUInteger)index
|
||||||
@@ -121,39 +92,41 @@
|
|||||||
|
|
||||||
- (void)invalidate
|
- (void)invalidate
|
||||||
{
|
{
|
||||||
_cachedPath = nil;
|
_cachedSize = NSZeroSize;
|
||||||
[self.parent invalidate];
|
[self.parent invalidate];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Calculation Methods
|
#pragma mark Calculation and Drawing Methods
|
||||||
- (NSSize)size
|
- (NSSize)size
|
||||||
{
|
{
|
||||||
return self.bezierPath.bounds.size;
|
if (NSEqualSizes(_cachedSize, NSZeroSize)) {
|
||||||
}
|
_cachedSize = [self generateSize];
|
||||||
|
|
||||||
- (NSBezierPath *)bezierPath
|
|
||||||
{
|
|
||||||
if (!_cachedPath) {
|
|
||||||
_cachedPath = [self generateBezierPath];
|
|
||||||
}
|
}
|
||||||
return _cachedPath;
|
return _cachedSize;
|
||||||
}
|
|
||||||
|
|
||||||
- (NSBezierPath *)bezierPathAtOrigin:(NSPoint)point
|
|
||||||
{
|
|
||||||
NSAffineTransform *transform = [NSAffineTransform transform];
|
|
||||||
[transform translateXBy:point.x
|
|
||||||
yBy:point.y];
|
|
||||||
NSBezierPath *path = [NSBezierPath bezierPath];
|
|
||||||
[path appendBezierPath:self.bezierPath];
|
|
||||||
[path transformUsingAffineTransform:transform];
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)drawAtPoint:(NSPoint)point
|
- (void)drawAtPoint:(NSPoint)point
|
||||||
{
|
{
|
||||||
NSBezierPath *path = [self bezierPathAtOrigin:point];
|
|
||||||
[path fill];
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MPLayout (MPSubclassImplement)
|
||||||
|
|
||||||
|
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSSize)sizeForChildAtIndex:(NSUInteger)index
|
||||||
|
{
|
||||||
|
return NSZeroSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSSize)generateSize
|
||||||
|
{
|
||||||
|
return NSZeroSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -16,21 +16,46 @@
|
|||||||
return (MPSumFunction *)self.function;
|
return (MPSumFunction *)self.function;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSBezierPath *)generateBezierPath
|
- (CTLineRef)line
|
||||||
{
|
{
|
||||||
NSAttributedString *text = [[NSAttributedString alloc] initWithString:@"∑"
|
CTLineRef line = [self lineForPrivateCacheIndex:0 generator:^CTLineRef{
|
||||||
attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}];
|
NSAttributedString *text =
|
||||||
self.textStorage.attributedString = text;
|
[[NSAttributedString alloc] initWithString:@"∑"
|
||||||
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
|
attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}];
|
||||||
NSGlyph glyphs[glyphRange.length+1];
|
CFAttributedStringRef attributedString = CFBridgingRetain(text);
|
||||||
NSUInteger actualGylphCount = [self.layoutManager getGlyphs:glyphs
|
CTLineRef line = CTLineCreateWithAttributedString(attributedString);
|
||||||
range:glyphRange];
|
CFRelease(attributedString); // TODO: Is this release appropriate
|
||||||
NSBezierPath *path = [NSBezierPath bezierPath];
|
return line;
|
||||||
[path moveToPoint:NSZeroPoint];
|
}];
|
||||||
[path appendBezierPathWithGlyphs:glyphs
|
return line;
|
||||||
count:actualGylphCount
|
}
|
||||||
inFont:[NSFont fontWithName:@"Lucida Grande" size:18.0]];
|
|
||||||
return path;
|
- (NSSize)generateSize
|
||||||
|
{
|
||||||
|
CTLineRef line = [self line];
|
||||||
|
CFRetain(line);
|
||||||
|
CGSize size = CTLineGetBoundsWithOptions(line, /*kCTLineBoundsUseOpticalBounds*/0).size;
|
||||||
|
CFRelease(line);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)drawAtPoint:(NSPoint)point
|
||||||
|
{
|
||||||
|
// Get the current context
|
||||||
|
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
||||||
|
|
||||||
|
// Set the text matrix
|
||||||
|
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
|
||||||
|
|
||||||
|
// Get the line of text
|
||||||
|
CTLineRef line = [self line];
|
||||||
|
CFRetain(line);
|
||||||
|
|
||||||
|
// Draw the line
|
||||||
|
CGContextSetTextPosition(context, point.x, point.y);
|
||||||
|
CTLineDraw(line, context);
|
||||||
|
|
||||||
|
CFRelease(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
Reference in New Issue
Block a user