[ Team LiB ] |
5.1 Text System ArchitectureThe following four classes make up the core architecture of Cocoa's text handling system:
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 patternFigure 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 NSTextViewNSTextView 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:
5.1.2 NSTextStorageNSTextStorage 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.
Figure 5-3. A tiny selection of Unicode characters in the Character PaletteNSTextStorage 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 NSLayoutManagerThe 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 glyphsThe 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.
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 NSTextContainerNSTextContainer 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 rectangular—strange, but true. Layout managers store text containers in an indexed array—the 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 OutWhen 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:
The method in NSTextContainer that performs these functions is: lineFragmentRectForProposedRect:sweepDirection: movementDirection:remainingRect: 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. |
[ Team LiB ] |