5.1 Text System Architecture

The following four classes make up the core architecture of
Cocoa's text
handling system:

NSTextStorage is the backbone data model responsible
for storing text.

NSTextView is responsible for presentation in the
view.

NSLayoutManager and
NSTextContainer act as controllers between the model and
the view.

The relationship between these core classes is based on the same
Model-View-Controller
(MVC) pattern used throughout the
Application Kit (and discussed in Chapter 3).
Figure 5-2 shows the division of responsibilities
in these four classes using the MVC pattern.

Figure 5-2. How the four core text system classes relate to one another in the MVC pattern

Figure 5-2 shows the relationship between the four
classes, but doesn't show the one-to-many
relationship that may exist between instances of these classes.
Instances of NSTextStorage own and manage one or
more NSLayoutManager objects. Similarly, each
instance of NSLayoutManager owns one or more
NSTextContainer objects, while each text container
object is paired with an NSTextView object. The
nature of these relationships is what gives Cocoa's
text handling system much of its flexibility and power.

5.1.1 NSTextView

NSTextView represents the view, or
presentation, layer of the text system; it
is the class that facilitates user interaction with the text system.
User interaction consists of displaying text onscreen and allowing
the user to manipulate what is seen in the text view.
NSTextView is a subclass of
NSText, which inherits from
NSView, which means that text rendering is handled
by Quartz. NSTextView provides support for more
advanced interactivity features such as drag and drop, rulers, spell
checking, cut-and-paste, and speech. It is not only the frontend to
the text system, but it is an interface between the text system and
almost every relevant Mac OS X technology.

You can create instances of NSTextView within
Interface Builder or by using one of two methods:

initWithFrame:

This method creates the entire network of objects, including the text
storage object, layout manager, and text container. When you create a
text view within Interface Builder, the entire collection of text
system objects is set up in this way.

initWithFrame:textContainer:

This method sets the text-view frame and associates the text view
with the specified text container. This method is the designated
initializer for NSTextView.

5.1.2 NSTextStorage

NSTextStorage makes up the data storage layer for the
text system. NSTextStorage's data
is stored as a sequence of Unicode characters, which makes the
text system capable of localizing an application in any language.
Unicode also contains character sets for mathematics and other
technical fields. To see the huge number of characters that Unicode
can represent,[1] launch Mac OS X's Character Palette from
the Input menu, as shown in Figure 5-3.

[1] You can find out more information
about Unicode, including the characters that can be represented, at
http://www.unicode.org.

Figure 5-3. A tiny selection of Unicode characters in the Character Palette

NSTextStorage is a subclass of
NSMutableAttributedString. Every character in the
text storage, therefore, is associated with a set of attributes that
define appearance characteristics such as font and color (a single
attributes dictionary will probably be applicable to a range of
characters, but it might have a different set of attributes for each
character). Cocoa defines a standard set of attributes, which were
enumerated in Table 4-2. Additionally, developers
may choose to assign their own application specific attributes to
text, which could support features such as syntax coloring.

As mentioned earlier, NSTextView contains action
methods that let controls change the appearance and layout of a
selected region of text. These action methods let controls in the
user interface (such as a bold-italic-underline button group, or the
font and color panels) interact with the contents of the text view.
However, using NSTextView's API
to effect these attribute changes programmatically is inefficient
since those methods are intended for use as user interface actions;
it is preferable to use the API provided by
NSMutableAttributedString. For example, the method
setAttributes:range: takes a dictionary with
attribute key-value pairs and a range to which these attributes
should be applied. Chapter 2 discusses attributed
strings in more detail.

5.1.3 NSLayoutManager

The job of
NSLayoutManager is to accurately map characters and
glyphs and lay out
the resulting glyphs in text containers managed by the layout
manager. Figure 5-4 shows a
ligature for
"Th" in the
font Snell
Roundhand and illustrates the mapping of characters into glyphs.

Figure 5-4. Mapping Unicode character codes into glyphs

The distinction between characters and glyphs is important, as it
represents the intersection between the
text-system's data and view layers. Glyphs, unlike
Unicode character codes, can take on many forms, the visual
appearance of which depends on the attributes of a particular
character such as its font, the other characters around that
character, and how ligatures are handled in the font being rendered.
For example, the glyph for the letter
"T" in the Times font is quite
different for the glyph the Zapfino font defines for the same letter.
Moreover, multiple characters in a sequence may actually define a
single glyph. This is especially true in nonwestern alphabets and in
fonts that define ligatures for certain pairs of letters.

You can find an extreme case of a multi-character glyph in the
Zapfino font. Open a TextEdit window, change the active font to
Zapfino, and then type the font's name.
You'll see multiple glyph substitutions as you type
the word, culminating in the use of a single glyph for the entire
wordZapfino's signature.

The flow of information with NSLayoutManager goes in two
directions. You just read about the flow from the data model to the
view; however, experience shows that information must flow from the
view to the data layer whenever you alter the content by typing,
making selections, or changing formatting. To facilitate this,
NSLayoutManager must be able reconcile the
position of selections and the cursor in the glyph stream with
character ranges in the storage layer.

The NSLayoutManager class has nearly 100 instance
methods. Most of these methods are responsible for mapping characters
to glyphs, setting attributes of glyphs, and controlling how they are
laid out in the view. The API discussed here are the methods that
control the text containers that define where text is laid out.

5.1.4 NSTextContainer

NSTextContainer defines regions for text display.
NSTextContainer's default
implementation defines rectangular text regions. However, developers
may subclass NSTextContainer to provide an
implementation that supports irregular layouts. For example, you
could subclass NSTextContainer to support text
layout on circular pages instead of rectangularstrange, but
true.

Layout
managers store text containers in an indexed arraythe order of
the text containers in the array is significant, as it determines the
order in which the layout manager fills the containers with text.
When the first container is filled with text, the layout manager
moves to the next, and continues with the remaining containers.

5.1.5 How Text Is Laid Out

When laying out
text, NSLayoutManager first converts a run of
characters into a mapped sequence of glyphs. Once the layout manager
knows exactly what needs to be laid out within a text container, it
can check with the text container object for guidance in this layout.
To do this, NSLayoutManager determines the
bounding rectangle of the line of glyphs and passes it to the current
text container as a proposed layout rectangle. The text container
looks at this proposed
rectangle and compares it to its own
bounding rectangle. For example, if the proposed
rectangle is too long, the text container returns the largest
available rectangle for the current line in the text container to the
layout manager. Additionally, the text container returns a
remainder rectangle, which is the difference
between the proposed rectangle and the accepted rectangle.
NSLayoutManager repeats the proposal process with
the remainder rectangle, and each successive remainder rectangle
until the layout is complete.

When determining how to modify the proposed rectangle,
NSTextContainer takes into account the direction
in which the glyphs are sequenced in a line, and the direction lines
are placed relative to their preceding lines. These directions are
referred to as the line sweep direction
and line movement
direction, respectively. When a text container
modifies the proposed rectangle, the text container can shorten the
rectangle from the direction of the line sweep, and it is allowed to
shift the rectangle in the direction of the line movement. By
adhering to these rules, NSTextContainer and
NSLayoutManager can break up a continuous line of
glyphs into an arranged set of lines that can be rendered in a view.
There is a clear division of responsibility here:

NSLayoutManager is responsible for mapping the
characters to glyphs with all the attributes applied.

NSTextContainer is used by the layout manager to
break up the glyph line into a series of lines that fit snugly into
the region represented by the text container.

The sweepDirection: argument is of type
NSLineSweepDirection, and the
movementDirection: argument is of type
NSLineMovementDirection.
NSTextContainer returns the remainder rectangle to
the sender through the remainingRect: argument,
which is a pointer to an NSRect structure.
Subclasses override this method to perform custom layout. If the text
container object determines that the proposed rectangle cannot fit
into the container, then the constant NSZeroRect
is returned.