The Layout Manager
The layout manager class,
NSLayoutManager, provides the central controlling object for text display in the Cocoa text system.
NSLayoutManager object performs the following actions:
Controls text storage and text container objects
Generates glyphs from characters
Computes glyph locations and stores the information
Manages ranges of glyphs and characters
Draws glyphs in text views when requested by the view
Manages rulers for paragraph style control
Computes bounding box rectangles for lines of text
Manipulates character and glyph attributes
In the model-view-controller paradigm,
NSLayoutManager is the controller.
NSTextStorage, a subclass of
NSMutableAttributedString, provides part of the model, holding a string of text characters with attributes such as typeface, style, color, and size.
NSTextContainer can also be considered part of the model because it models the geometric layout of the page on which the text is laid out.
NSTextView (or another
NSView object) provides the view in which the text is displayed.
NSLayoutManager serves as the controller for the text system because it directs the glyph generator to translate characters in the text storage object into glyphs, directs the typesetter to lay them out in lines according to the dimensions of one or more text container objects, and coordinates the text display in one or more text view objects. Figure 1 illustrates the composition of the text display, which is coordinated by the layout manager.
You can configure the text system to have more than one layout manager if you need the text in the same
NSTextStorage object to be laid out in more than one way. For example, you might want the text to appear as a continuous galley in one view and to be segmented into pages in another view. For more information about different arrangements of text objects, refer to Common Configurations.
Generally speaking, a given layout manager (and associated objects) should not be used on more than one thread at a time. Most layout managers are used on the main thread, since it is the main thread on which their text views are displayed, and since background layout occurs on the main thread. However, you can lay out and render text on secondary threads using
NSLayoutManager as long as the object graph is contained in a single thread.
If you must use a layout manager on a secondary thread, it's the application's responsibility to ensure that the objects are not accessed simultaneously from other threads. First, make sure that
NSTextView objects associated with that layout manager (if any) are not displayed while the layout manager is being used on the secondary thread by disabling background layout and auto-display. For example, you could send the text view
lockFocusIfCanDraw to block the main thread display (and send
unlockFocus when finished). Second, turn off background layout for that layout manager while it is being used on the secondary thread by sending
The Layout Process
The layout manager performs text layout in two separate steps: glyph generation and glyph layout. The layout manager performs both layout steps lazily, that is, on an as-needed basis. Accordingly, some
NSLayoutManager methods cause glyph generation to happen, while others do not, and the same is true with glyph layout. After it generates glyphs and after it calculates their layout locations, the layout manager caches the information to improve performance of subsequent invocations.
The layout manager caches glyphs, attributes, and layout information. It keeps track of ranges of glyphs that have been invalidated by changes to the characters in the text storage. There are two ways in which a character range can be automatically invalidated: if it needs glyphs generated or if it needs glyphs laid out. If you wish, you can manually invalidate either glyph or layout information. When the layout manager receives a message requiring knowledge of glyphs or layout in an invalidated range, it generates the glyphs or recalculates the layout as necessary.
NSLayoutManager uses an
NSTypesetter object to perform the actual glyph layout. See Typesetters for more information. Figure 2 illustrates the interaction of objects involved in the layout process.
The following steps, numbered to correlate with the numbers in Figure 2, explain how the layout manager controls text layout:
Text in the text storage changes, invalidating glyphs or their layout positions or both. Invalidation occurs, for example, because the user edits the text in a text view, and the text view causes the changes to the contents of the text storage. Or another object can change the text programmatically.
The text storage notifies its associated layout manager (or managers) of the invalidated character range by sending the message
textStorage:edited:range:changeInLength:invalidatedRange:. The message specifies whether the change affected characters, attributes, or both; the range of characters that changed; and the range affected after attribute fixing.
The layout manager updates its internal data structures to reflect the invalid range. Attribute changes may or may not affect glyph generation and layout. For example, changing the color of text does not affect how it gets laid out.
To notify its associated text views that they need to redisplay the invalidated area, the layout manager sends the message
setNeedsDisplayInRect:avoidAdditionalLayout:. At this point any of several things could happen. If the invalidated portion of the text view is visible, the text view asks the layout manager for any needed glyphs and their positions. If the invalidated area is not currently visible, the view does not immediately call for layout. However, background (idle-time) layout may occur when the application has no events to process. Background layout is on by default, although you can turn it off for any individual layout manager.
When the text view asks the layout manager for glyphs and positions, the layout process begins. (Other messages can also invoke layout. The layout manager header file and reference documentation specify which methods cause glyph generation and layout to occur.)
The layout manager generates a stream of glyphs from the newly edited character range and caches the glyphs. Glyph generation is a quick first-pass conversion of characters in a particular font to glyphs. (Without font information, glyphs cannot be generated.) Later stages of the layout process can make changes to the glyph stream.
After the required glyphs have been generated, the layout manager calls its typesetter to lay out the glyphs into one or more line fragments, sending the
layoutGlyphsInLayoutManager:startingAtGlyphIndex:maxNumberOfLineFragments:nextGlyphIndex:message to the typesetter. During this process, the typesetter may perform glyph substitution; for example, it can substitute a ligature glyph in place of two or more single-character glyphs.
The typesetter generates line fragment rectangles in communication with the text container and determines the placement of each glyph, as described in Typesetters and Line Fragment Generation.
The typesetter sends the line fragment rectangles with their glyphs and positions to the layout manager, which commits the information in its internal data structures as valid layout.
In addition to generating glyphs and performing layout, the layout manager does the drawing of the glyphs in the text view. Drawing occurs when the text view asks the layout manager to figure out which glyphs lie within a given view rectangle and to display them. The layout manager has methods for drawing glyphs and their background. These methods do all the necessary drawing by calling into the Quartz graphic layer. They draw the background, set up the font and color, and draw the glyphs, underlines, and any temporary attributes.
NSLayoutManager methods use container coordinates, rather than view coordinates. The text system expects view coordinates to be flipped, like those of
NSTextView. If you have a point in view coordinates that you need to convert to container coordinates, subtract the text view’s
textContainerOrigin value to get the container coordinates. Glyph locations are expressed relative to their line fragment bounding rectangle’s origin. Figure 3 shows the relationships among these coordinate systems.
Attributes are qualities the layout manager applies to characters during typesetting and layout, such as font, size, and color. The text storage preserves many attributes in a dictionary stored with the character string, but other attributes are temporary and maintained only by the layout manager during the layout process. Temporary attributes supersede attributes associated with the font or paragraph. The drawing methods also handle additional attributes associated with the text view—for example, a different background color—when the glyphs being drawn are selected in the text view. The drawing methods call some other public
NSLayoutManager methods, such as
drawUnderlineForGlyphRange:underlineType:baselineOffset:lineFragmentRect:lineFragmentGlyphRange:containerOrigin:, which you can override if you want to do things differently. See Text Attribute Programming Topics for more information.
The layout manager also handles the representation of attachments during glyph drawing. The text system stores an attachment as an attribute of a special character. A typical attachment is a file, but it can also be in-memory data. A file attachment is usually handled by drawing an icon. However, an attachment can do much more than that if you implement different behavior. During layout the attachment cell (
NSTextAttachmentCell) tells the layout manager what size it is, so it can be laid out like a glyph. Accordingly, the text’s line height and character placement are adjusted to accommodate the attachment cell. During drawing, the layout manager asks the attachment cell to draw itself. See Text Attachment Programming Topics for more information.
The layout manager retains and reuses as much layout information as possible, to minimize recalculating glyph positions. For example, if the glyphs have already been generated for an invalidated character range that needs to be laid out, the layout manager tries to optimize the layout process. In the best case, such holes in the layout can be filled just by shifting line fragment locations within the text container.
NSLayoutManager provides a public API for getting glyphs from characters. The process is complex, however: You cannot simply convert a single character into a glyph because the relationship between characters and glyphs is many-to-many. That is, one character in the text storage can map to multiple glyphs and vice versa. Therefore, you use the
glyphRangeForTextContainer: to get the glyphs for all the characters laid out in a text container or
glyphRangeForCharacterRange:actualCharacterRange: for a range of characters.