651 lines
22 KiB
Objective-C
651 lines
22 KiB
Objective-C
//
|
|
// MPLayout.h
|
|
// MathKit
|
|
//
|
|
// Created by Kim Wittenburg on 07.08.14.
|
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
|
//
|
|
|
|
|
|
/*!
|
|
@header
|
|
This file contains the <code>MPLayout</code> class.
|
|
|
|
The <code>MPLayout</code> class renders an expression to be displayed in an
|
|
<code>@link //apple_ref/occ/cl/MPExpressionView@/link</code>. There are multiple
|
|
subclasses of <code>MPLayout</code> the most important of which are <code>@link
|
|
//apple_ref/occ/cl/MPExpressionLayout@/link</code> and <code>@link
|
|
//apple_ref/occ/cl/MPFunctionLayout@/link</code> for rendering expressions and
|
|
functions respectively.
|
|
|
|
Both types of layouts can occur in two different sizes: normal and small. The
|
|
root expression ueses the normal size. Depending on the concrete subclass of
|
|
<code>@link //apple_ref/occ/cl/MPFunctionLayout@/link</code> that is used a
|
|
function's children (and any subsequent children) may be rendered using the
|
|
small size. Also there are two different fonts available for layouts: a normal
|
|
and a special font. The special font should only be used to emphasize special
|
|
content that uses the normal font.
|
|
|
|
If an expression is empty an empty box is rendered instead. That box (called the
|
|
<i>empty box</i> uses the font metrics of the empty expression it represents.
|
|
That means it uses different sizes depending on whether the expression uses a
|
|
small or normal sized font. Also it is drawn a little smaller than the actual
|
|
box's height is because it looks nicer. Depending on your needs you can query
|
|
the metrics of the empty box using the <code>kMPEmptyBox...</code> or
|
|
<code>kMPEmptyBoxDrawing...</code> macros. These macros must not be used outside
|
|
of the <code>MPLayout</code> class or any of it subclasses.
|
|
*/
|
|
|
|
|
|
/*!
|
|
@define kMPEmptyBoxWidth
|
|
@abstract The width of the empty box.
|
|
*/
|
|
#define kMPEmptyBoxWidth (self.usesSmallSize ? 2.0 : 3.0)
|
|
|
|
|
|
/*!
|
|
@define kMPEmptyBoxHeight
|
|
@abstract The actual height of the empty box.
|
|
*/
|
|
#define kMPEmptyBoxHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font))
|
|
|
|
|
|
/*!
|
|
@define kMPEmptyBoxYOrigin
|
|
@abstract The actual vertical origin of the empty box.
|
|
|
|
@discussion The vertical origin is a negative value to be compliant with the
|
|
metrics of strings.
|
|
*/
|
|
#define kMPEmptyBoxYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)))
|
|
|
|
|
|
/*!
|
|
@define kMPEmptyBoxDrawingWidth
|
|
@abstract The with the empty box is rendered with.
|
|
*/
|
|
#define kMPEmptyBoxDrawingWidth kMPEmptyBoxWidth
|
|
|
|
|
|
/*!
|
|
@define kMPEmptyBoxDrawingHeight
|
|
@abstract The height the empty box is drawn with.
|
|
*/
|
|
#define kMPEmptyBoxDrawingHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font))
|
|
|
|
|
|
/*!
|
|
@define kMPEmptyBoxDrawingYOrigin
|
|
@abstract The vertical origin the empty box is drawn at.
|
|
|
|
@discussion The vertical origin is a negative value to be compliant with the
|
|
metrics of strings.
|
|
*/
|
|
#define kMPEmptyBoxDrawingYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)/2))
|
|
|
|
|
|
|
|
@class MPLayout, MPExpressionView, MPRangePath;
|
|
|
|
|
|
/*!
|
|
@class MPLayout
|
|
@abstract This is an abstract class that defines the basic methods that
|
|
must be implemented by other layout classes. It also defines some
|
|
useful helper methods.
|
|
|
|
@discussion The <code>MPLayout</code> class maintains a generic cache. It is
|
|
used to store child layouts and actual cache information.
|
|
|
|
Every layout instance represents either an expression or a
|
|
function. The instance's children represent the children of the
|
|
represented object.
|
|
*/
|
|
@interface MPLayout : NSObject
|
|
|
|
#pragma mark Creation Methods
|
|
/*!
|
|
@methodgroup Creation Methods
|
|
*/
|
|
|
|
|
|
/*!
|
|
@method init
|
|
@abstract Initializes the receiver.
|
|
|
|
@discussion This method should only be used to initialize the expression
|
|
layout that renders the root layout.
|
|
|
|
This method is the designated initializer of the
|
|
<code>MPLayout</code> class.
|
|
|
|
@return A newly initialized <code>MPLayout</code> instance.
|
|
*/
|
|
- (instancetype)init;
|
|
|
|
|
|
/*!
|
|
@method initWithParent:
|
|
@abstract Initializes the receiver with the specified parent.
|
|
|
|
@discussion This method should generally be used when initializing layout
|
|
instances. In most cases it should be called at some point in the
|
|
implementation of the <code>@link
|
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/childLayoutAtIndex:@/link</code>
|
|
method.
|
|
|
|
@param parent
|
|
The receiver's parent.
|
|
|
|
@return A newly initialized <code>MPLayout</code> instance.
|
|
*/
|
|
- (instancetype)initWithParent:(MPLayout *)parent;
|
|
|
|
|
|
#pragma mark Properties
|
|
/*!
|
|
@methodgroup Properties
|
|
*/
|
|
|
|
|
|
/*!
|
|
@property expressionView
|
|
@abstract The receiver's expression view.
|
|
|
|
@discussion The expression view is the view in which the receivin
|
|
<code>MPLayout</code> instance draws itself into. Normally you
|
|
should not call the setter of this property. It is automatically
|
|
assigned when the <code>@link
|
|
//apple_ref/occ/instp/MPExpressionView/expressionStorage@/link</code>
|
|
property of the respective <code>@link
|
|
//apple_ref/occ/cl/MPExpressionView@/link</code> instance is set.
|
|
*/
|
|
@property (nonatomic, weak) MPExpressionView *expressionView;
|
|
|
|
|
|
/*!
|
|
@method font
|
|
@abstract Returns the receiver's context inferred font.
|
|
|
|
@discussion The context inferred font is the normal font using the context
|
|
inferred font size. Normally this method is used for any textual
|
|
content of a layout.
|
|
|
|
@return The font the receiver should use for rendering textual content.
|
|
*/
|
|
- (NSFont *)font;
|
|
|
|
|
|
/*!
|
|
@method normalFontWithSize:
|
|
@abstract Returns a <code>NSFont</code> object that represents the normal
|
|
font in the specified <code>size</code>.
|
|
|
|
@discussion Instead of this method you want to use <code>@link
|
|
//apple_ref/occ/instm/MPLayout/font@/link</code> instead in most
|
|
cases.
|
|
|
|
@param size
|
|
The size of the font.
|
|
|
|
@return A <code>NSFont</code> object that can be used to render textual
|
|
content of the receiver in the specified <code>size</code>.
|
|
*/
|
|
- (NSFont *)normalFontWithSize:(CGFloat)size;
|
|
|
|
|
|
/*!
|
|
@method specialFontWithSize:
|
|
@abstract Returns a <code>NSFont</code> object that represents the special
|
|
font in the specified <code>size</code>.
|
|
|
|
@discussion Use fonts returned by this method only if you need to emphasize
|
|
text that is surrounded by other text that uses the normal font.
|
|
|
|
@param size
|
|
The size of the font.
|
|
|
|
@return A <code>NSFont</code> object that can be used to render special
|
|
textual content of the receiver in the specified
|
|
<code>size</code>.
|
|
*/
|
|
- (NSFont *)specialFontWithSize:(CGFloat)size;
|
|
|
|
|
|
/*!
|
|
@method contextInferredFontSize
|
|
@abstract Returns the appropriate font size for the receiver based on the
|
|
context it will be rendered in.
|
|
*/
|
|
- (CGFloat)contextInferredFontSize;
|
|
|
|
|
|
/*!
|
|
@method normalFontSize
|
|
@abstract Returns the font size that is used in the root expression.
|
|
*/
|
|
- (CGFloat)normalFontSize;
|
|
|
|
|
|
/*!
|
|
@method smallFontSize
|
|
@abstract Returns the font size that is used in expressions that are using
|
|
the small size.
|
|
*/
|
|
- (CGFloat)smallFontSize;
|
|
|
|
|
|
#pragma mark Cache Tree
|
|
/*!
|
|
@methodgroup Cache Tree
|
|
*/
|
|
|
|
|
|
/*!
|
|
@property parent
|
|
@abstract The receiver's parent layout.
|
|
|
|
@discussion The parent layout is most likely the one that created the
|
|
receiver and is responsible for setting its attributes
|
|
appropriately based on the rendering context.
|
|
*/
|
|
@property (readonly, nonatomic, weak) MPLayout *parent;
|
|
|
|
|
|
#pragma mark Cache Methods
|
|
/*!
|
|
@methodgroup Cache Methods
|
|
*/
|
|
|
|
|
|
/*!
|
|
@method chacableObjectForIndex:generator:
|
|
@abstract Returns the cached object for the specified <code>index</code> or
|
|
creates one if it does not exist.
|
|
|
|
@discussion This method only returns <code>nil</code> if the
|
|
<code>generator</code> returns <code>nil</code>.
|
|
|
|
@param index
|
|
The index of the cached object to retrieve.
|
|
|
|
@param generator
|
|
This block is executed if there is no cached object at
|
|
<code>index</code>. The result of this block is then cached at
|
|
the <code>index</code>.
|
|
|
|
@return The cached object for <code>index</code> or the result of the
|
|
<code>generator</code> if there is no cached object.
|
|
*/
|
|
- (id)cachableObjectForIndex:(NSUInteger)index
|
|
generator:(id(^)())generator;
|
|
|
|
|
|
/*!
|
|
@method clearCacheInRange:replacementLength:
|
|
@abstract Removes the objects in the specified from the cache and moves all
|
|
objects at subsequent indexes.
|
|
|
|
@discussion The objects at subsequent indexes are moved
|
|
<code>replacementLength - range.length</code> places. If
|
|
<code>replacementLength</code> is greater than
|
|
<code>range.length</code> they are moved to smaller indexes.
|
|
|
|
@param range
|
|
The range of objects to be removed.
|
|
|
|
@param replacementLength
|
|
The number of indexes replacing the ones specified by
|
|
<code>range</code>.
|
|
*/
|
|
- (void)clearCacheInRange:(NSRange)range
|
|
replacementLength:(NSUInteger)replacementLength;
|
|
|
|
|
|
/*!
|
|
@method invalidate
|
|
@abstract Invalidates the receiver.
|
|
|
|
@discussion Invalidating the receiver causes him to discard all cached
|
|
information about itself and send a <code>invalidate</code>
|
|
message to its parent. The cached information about the
|
|
receiver's children is <b>not</b> discarded. Use <code>@link
|
|
//apple_ref/occ/instm/MPLayout/clearCacheInRange:replacementLength:@/link</code>
|
|
for that purpose.
|
|
*/
|
|
- (void)invalidate;
|
|
|
|
|
|
/*!
|
|
@method childLayoutAtIndexPath:
|
|
@abstract Returns the child layout at the specified index path.
|
|
|
|
@discussion If the <code>indexPath</code> is empty the receiver is returned.
|
|
If the child layout the index path specifies is not yet created
|
|
this method should do so and add it to the cache of the
|
|
respective parent layout.
|
|
|
|
@param indexPath
|
|
The index path of the child.
|
|
|
|
@return The child at the specified index path.
|
|
*/
|
|
- (MPLayout *)childLayoutAtIndexPath:(NSIndexPath *)indexPath;
|
|
|
|
|
|
#pragma mark Rendering Methods
|
|
/*!
|
|
@methodgroup Rendering Methods
|
|
*/
|
|
|
|
|
|
/*!
|
|
@method createLineForString:
|
|
@abstract Renders the specified string.
|
|
|
|
@discussion This method uses the normal font and the context inferred font
|
|
size to render the string. In most cases this is the appropriate
|
|
method for rendering textual content in a layout.
|
|
|
|
@param aString
|
|
The string to be rendered.
|
|
|
|
@return A <code>CTLineRef</code> representing the rendered string.
|
|
*/
|
|
- (CTLineRef)createLineForString:(NSString *)aString;
|
|
|
|
|
|
/*!
|
|
@method createLineForString:emphasize:
|
|
@abstract Renders the specified string.
|
|
|
|
@discussion This method uses either the normal or the special font depending
|
|
on the <code>emphasize</code> flag. It uses the context inferred
|
|
font size.
|
|
|
|
@param aString
|
|
The string to be rendered.
|
|
|
|
@param emphasize
|
|
A flag indicating whether the special font should be used.
|
|
|
|
@return A <code>CTLineRef</code> representing the rendered string.
|
|
*/
|
|
- (CTLineRef)createLineForString:(NSString *)aString emphasize:(BOOL)emphasize;
|
|
|
|
|
|
/*!
|
|
@method createLineForString:usingFont:
|
|
@abstract Renders the specified string in the specified font.
|
|
|
|
@discussion In most cases you do should prefer <code>@link
|
|
//apple_ref/occ/instm/MPLayout/createLineForString:@/link</code>
|
|
or <code>@link
|
|
//apple_ref/occ/instm/MPLayout/createLineForString:emphasize:@/link</code>
|
|
over this method.
|
|
|
|
@param aString
|
|
The string to be rendered.
|
|
|
|
@param font
|
|
The font to render <code>aString</code> in.
|
|
|
|
@return A <code>CTLineRef</code> representing the rendered string.
|
|
*/
|
|
- (CTLineRef)createLineForString:(NSString *)aString usingFont:(NSFont *)font;
|
|
|
|
|
|
/*!
|
|
@property flipped
|
|
@abstract This property indicates whether the receiver uses a flipped
|
|
layout.
|
|
|
|
@discussion The value of this property should be the same in the receiver,
|
|
its parent and all of its children.
|
|
*/
|
|
@property (nonatomic, getter = isFlipped) BOOL flipped;
|
|
|
|
|
|
/*!
|
|
@property usesSmallSize
|
|
@abstract This property indicates whether the receiver uses the small font
|
|
size.
|
|
|
|
@discussion This property is used to determine the <code>@link
|
|
//apple_ref/occ/instm/MPLayout/contextInferredFontSize@/link</code>.
|
|
If this value is <code>YES</code> all of the receiver's children
|
|
should also use the small size. If not the children may or may
|
|
not (depending on the actual layout implementation) use the small
|
|
size.
|
|
*/
|
|
@property (nonatomic) BOOL usesSmallSize;
|
|
|
|
|
|
/*!
|
|
@method bounds
|
|
@abstract Returns the receiver's bounds.
|
|
|
|
@discussion If the receiver has cached bounds it will return the cached
|
|
value. The cached value can be removed using the <code>@link
|
|
//apple_ref/occ/instm/MPLayout/invalidate@/link</code> method. If
|
|
there is no cached value this method will generate a new cached
|
|
value by calling the <code>@link
|
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/generateBounds@/link</code>
|
|
method.
|
|
|
|
@return The receiver's bounds.
|
|
*/
|
|
- (NSRect)bounds;
|
|
|
|
|
|
/*!
|
|
@method boundingRectForRangePath:
|
|
@abstract Returns the rect relative to the origin of the receiver's bounds
|
|
that contains the symbols identified by the specified range path.
|
|
|
|
@discussion Use this method to determine where the user's selection should be
|
|
drawn at.
|
|
|
|
@param rangePath
|
|
The range path whose bounds are needed.
|
|
|
|
@return The rectangle containing the symbols identified by
|
|
<code>rangePath</code>.
|
|
*/
|
|
- (NSRect)boundingRectForRangePath:(MPRangePath *)rangePath; /* if rangePath.length is 0 the returned rect will have a width of 0 */
|
|
|
|
|
|
/*!
|
|
@method drawAtPoint:
|
|
@abstract Draws the receiver at the specified point.
|
|
|
|
@discussion If the receiver returns <code>NO</code> from its <code>@link
|
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
|
|
method this method also draws every single child of the receiver.
|
|
The location the children are drawn at is determined by the
|
|
<code>@link
|
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/offsetOfChildLayoutAtIndex:@/link</code>
|
|
method.
|
|
|
|
@param point
|
|
The point the receiver is to be drawn at.
|
|
*/
|
|
- (void)drawAtPoint:(NSPoint)point;
|
|
|
|
@end
|
|
|
|
|
|
|
|
/*!
|
|
@category MPLayout (MPSubclassImplement)
|
|
@abstract The methods in this category must be implemented by any concrete
|
|
subclass of <code>MPLayout</code>.
|
|
*/
|
|
@interface MPLayout (MPSubclassImplement)
|
|
|
|
/*!
|
|
@method numberOfChildren
|
|
@abstract Returns the number of sublayouts of the receiver.
|
|
|
|
@discussion This method must be implemented by subclasses.
|
|
*/
|
|
- (NSUInteger)numberOfChildren;
|
|
|
|
|
|
/*!
|
|
@method drawsChildrenManually
|
|
@abstract Returns whether the receiver wants to take over responsibility of
|
|
drawing its children.
|
|
|
|
@discussion If you return <code>YES</code> from this method
|
|
<code>MPLayout</code> does not draw the receiver's children. The
|
|
receiver's <code>//apple_ref/occ/intfm/MPLayout/draw</code>
|
|
method has to implement that functionality. If you return
|
|
<code>NO</code> from this method the receiver must not draw its
|
|
children in the <code>//apple_ref/occ/intfm/MPLayout/draw</code>
|
|
method.
|
|
|
|
This method may be implemented by subclasses to opt in and change
|
|
the default behaviour.
|
|
|
|
@return <code>YES</code> if the receiver draws its children
|
|
automatically, <code>NO</code> otherwise. The default
|
|
implementation returns <code>NO</code>.
|
|
*/
|
|
- (BOOL)drawsChildrenManually;
|
|
|
|
|
|
/*!
|
|
@method childLayoutAtIndex:
|
|
@abstract Returns the receiver's child at the specified index.
|
|
|
|
@discussion As long as the specified <code>index</code> does not exceed the
|
|
receiver's number of children, this method should always return a
|
|
value based on the following criteria:
|
|
1. If the receiver represents a <code>@link
|
|
//apple_ref/occ/cl/MPFunction@/link</code> instance it should
|
|
always return a valid <code>MPLayout</code> instance.
|
|
2. If the receiver represents a <code>@link
|
|
//apple_ref/occ/cl/MPExpression@/link</code> instance it
|
|
returns only a valid <code>MPLayout</code> instance if the
|
|
represented expression's element at <code>index</code> is a
|
|
function.
|
|
|
|
This method should cache any generated child layout and use the
|
|
cache whenever possible in order to reduce resources needed for
|
|
rendering.
|
|
|
|
This method must be implemented by subclasses.
|
|
|
|
@param index
|
|
The index of the requested child layout.
|
|
|
|
@return A <code>MPLayout</code> instance representing the respective
|
|
child of the object that is represented by the receiver, or
|
|
<code>nil</code> if the child represents a string element in an
|
|
expression.
|
|
*/
|
|
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index;
|
|
|
|
|
|
/*!
|
|
@method generateBounds
|
|
@abstract Calculates the receiever's bounds.
|
|
|
|
@discussion This method should not use any cached values. Caching of the
|
|
calculated bounds is done automatically by the
|
|
<code>MPLayout</code> class. This method is only called if the
|
|
receiving <code>MPLayout</code> instance has been invalidated and
|
|
the receiver's bounds have been requested.
|
|
|
|
This method must be implemented by subclasses.
|
|
|
|
@return The receiver's bounds.
|
|
*/
|
|
- (NSRect)generateBounds;
|
|
|
|
|
|
/*!
|
|
@method boundingRectForRange:
|
|
@abstract Returns the rectangle that encloses the symbols in the specified
|
|
range.
|
|
|
|
@discussion This method is called to calculate the rectangle that encloses
|
|
the user's selection. If the specified <code>range</code> has a
|
|
length of <code>0</code> the returned rectangle should be the
|
|
bounds of the caret (which has a width of <code>0</code>). The
|
|
height and y-origin of the returned rectangle should be equal to
|
|
the respective values of the receiver.
|
|
|
|
This method must be implemented by subclasses.
|
|
|
|
@param range
|
|
The range of the selection whose bounds should be calculated.
|
|
|
|
@return A rectangle enclosing the user's selection.
|
|
*/
|
|
- (NSRect)boundingRectForRange:(NSRange)range;
|
|
|
|
|
|
/*!
|
|
@method offsetOfChildLayoutAtIndex:
|
|
@abstract Returns the location of the child layout at the specified index.
|
|
|
|
@discussion This method must be implemented by subclasses even if the
|
|
subclass returns <code>YES</code> from the <code>@link
|
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
|
|
method.
|
|
|
|
@param index
|
|
The index of the child whose location is to be calculated.
|
|
|
|
@return The location of the child layout at the specified index relative
|
|
to the receiver.
|
|
*/
|
|
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index;
|
|
|
|
|
|
/*!
|
|
@method indexPathForMousePoint:
|
|
@abstract Performs hit testing.
|
|
|
|
@discussion This method tests the specified <code>point</code> against all of
|
|
its children and returns the index path (relative to the index
|
|
path of the receiver) that identifies the location inside the
|
|
expression tree the user selected. The specified point must be
|
|
inside of the receiver and must be specified relatively to the
|
|
receiver. If the specified point is not inside any of the
|
|
receiver's children but in the receiver itself the returned index
|
|
path must contain one index specifiying the location in the
|
|
receiver.
|
|
|
|
This method must be implemented by subclasses.
|
|
|
|
@param point
|
|
The point inside the receiver that is to be tested.
|
|
|
|
@return The index path that points to the point the user selected. All
|
|
indexes in the path except for the last one are specified in the
|
|
element reference frame. The last index is specified in the
|
|
symbol reference frame.
|
|
*/
|
|
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point;
|
|
|
|
|
|
/*!
|
|
@method draw
|
|
@abstract Draws the receiver.
|
|
|
|
@discussion If the receiver returns <code>YES</code> from its <code>@link
|
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
|
|
method the implementation of this method must also draw the
|
|
receiver's children.
|
|
|
|
This method must be implemented by subclasses. If the receiver
|
|
does not have anything to draw except for its children you should
|
|
implement this method with an empty method body.
|
|
*/
|
|
- (void)draw;
|
|
|
|
@end |