Archived
1
This repository has been archived on 2022-08-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
mathpad/MathKit/MPLayout.h
2015-01-08 21:58:15 +01:00

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