15.3 The Text Cursor

  • 15.3.1 Text Cursor Protocol
  • 15.3.2 Stream Text Cursor Protocol
  • In the days when display devices displayed only two dimensional arrays of fixed width characters, the text cursor was a simple thing. A discrete position was selected in integer character units, and a character could go there and noplace else. Even for variable width fonts, simply addressing a character by the pixel position of one of its corners is sufficient. However, variable height fonts with variable baselines on pixel-addressable displays upset this simple model. The "logical" vertical reference point is the baseline, as it is in typesetting. In typesetting, however, an entire line of text is created with baselines aligned and padded to the maximum ascent and descent, then the entire line is put below the previous line. [annotate]

    It is clearly desirable to have the characters on a line aligned with their baselines, but when the line on the display is formed piece by piece, it is impossible to pick in advance the proper baseline. The solution CLIM adopts is to choose a baseline, but not commit to it. [annotate]

    The CLIM model says that text has at least 6 properties. With a reference point of (0,0) at the upper left of the text, it has a bounding box consisting of ascent, descent, left kerning, right extension, and a displacement to the next reference point in both x and y. CLIM determines the position of the reference point and draws the text relative to that, and then the cursor position is adjusted by the displacement. In this way, text has width and height, but the x and y displacements need not equal the width and height. [annotate]

    CLIM adopts the following approach to the actual rendering of a glyph. Textual output using the stream functions (not the graphics functions) maintains text on a "line". Note that a line is not an output record, but is rather a collection of "text so far", a top (which is positioned at the bottom of the previous line plus the stream's vertical spacing), a baseline, a bottom, and a "cursor position". The cursor position is defined to be at the top of the line, not at the baseline. The reason for this is that the baseline can move, but the top is relative to the previous line, which has been completed and therefore doesn't move. If text is drawn on the current line whose ascent is greater than the current ascent of the line, then the line is moved down to make room. This can be done easily using the output records for the existing text on the line. When there is enough room, the reference point for the text is the x position of the cursor at the baseline, and the cursor position is adjusted by the displacement. [annotate]

    The following figures show this in action before and after each of three characters are drawn. In all three figure, the small circle is the "cursor position". At first, there is nothing on the line. The first character establishes the initial baseline, and is then drawn. The upper left corner of the character is where the cursor was (as in the traditional model), but this will not remain the case. Drawing the second character, which is larger than the first, requires moving the first character down in order to get the baselines to align; during this time, the top of the line remains the same. Again, the upper left of the second character is where the cursor was, but that is no longer the case for the first character (which has moved down). The third character is smaller than the second, so no moving of characters needs to be done. However, the character is drawn to align the baselines, which in this case means the upper left is not where the cursor was. Nor is the cursor at the upper right of the character as it was for the previous two characters. It is, however, at the upper right of the collective line. [annotate]

    (50,60)(-4,0) (0,30) (10,30)(0,-20) (0,0)(1,0)4 (-2,2)(0,0)[r](0,y) (-2,2)(1,-1)2 (0,0).5 (10,30) (10,30)(0,-20) (0,0)(1,0)12 (-2,2)(0,0)[r](0,y) (-2,2)(1,-1)2 (0,-12)(8,12)small (4,4)(0,0)[r](w1,y) (4,4)(1,-1)4 (8,0).5 (-2,-16)(0,0)[r]bl-small (-2,-16)(1,3)2 (0,-10)(1,0)10 (30,30) (10,30)(0,-20) (0,0)(1,0)22 (-2,2)(0,0)[r](0,y) (-2,2)(1,-1)2 (0,-14)(8,12)small (4,4)(0,0)[r](w1,y) (4,4)(1,-1)4 (8,-16)(10,16)BIG (12,6)(0,0)[r](w1+w2,y) (12,6)(1,-1)6 (18,0).5 (-2,-18)(0,0)[r]bl-BIG (-2,-18)(1,3)2 (0,-12)(1,0)20 (10,0) (10,30)(0,-20) (0,0)(1,0)30 (-2,2)(0,0)[r](0,y) (-2,2)(1,-1)2 (0,-14)(8,12)small (4,4)(0,0)[r](w1,y) (4,4)(1,-1)4 (8,-16)(10,16)BIG (12,6)(0,0)[r](w1+w2,y) (12,6)(1,-1)6 (18,-14)(8,12)small (18,8)(0,0)[r](w1+w2+w3,y) (18,8)(1,-1)8 (26,0).5 (-2,-18)(0,0)[r]bl-BIG (-2,-18)(1,3)2 (0,-12)(1,0)28 [annotate]

    Minor issue: The above may be too simplistic. The displacement probably wants to depend not only on language but language rendering mode. For example, Japanese can apparently go either vertically or horizontally. It may be necessary to have the bounding box and perhaps the reference point dispatch as well. Similarly, "newline" could mean "down one line, all the way to the left" for English, "down one line, all the way to the right" for Arabic, or "to the left one line, all the way to the top." "Home cursor" is another ditty to consider. We need to discuss this on a larger scale with people who know multi-lingual rendering issues. --- DCPL [annotate]

    15.3.1 Text Cursor Protocol

    Many streams that maintain a text cursor display some visible indication of the text cursor. The object that represents this display is (somewhat confusingly) also called a cursor. [annotate]

    An active cursor is one that is being actively maintained by its owning sheet. A active cursor has a state that is either on or off. An active cursor can also has a state that indicates the the owning sheet has the input focus. [annotate]

    cursor   [Protocol Class]
              

    The protocol class that corresponds to cursors. If you want to create a new class that behaves like a cursor, it should be a subclass of cursor. All instantiable subclasses of cursor must obey the cursor protocol. Members of this class are mutable. [annotate]

    [annotate]

    cursorp  object [Protocol Predicate]
              

    Returns true if object is a cursor, otherwise returns false. [annotate]

    [annotate]

    :sheet   [Initarg]
              

    The :sheet initarg is used to specify the sheet with which the cursor is associated. [annotate]

    [annotate]

    standard-text-cursor   [Class]
              

    The instantiable class that implements a text cursor. Typically, ports will further specialize this class. [annotate]

    [annotate]

    cursor-sheet  cursor [Generic Function]
              

    Returns the sheet with which the cursor cursor is associated. [annotate]

    [annotate]

    cursor-position  cursor [Generic Function]
              

    Returns the x and y position of the cursor cursor as two values. x and y are in the coordinate system of the cursor's sheet. [annotate]

    [annotate]

    (setf* cursor-position)  x y cursor [Generic Function]
              

    Sets the x and y position of the cursor cursor to the specified position. x and y are in the coordinate system of the cursor's sheet. [annotate]

    For CLIM implementations that do not support setf*, the "setter" function for this is cursor-set-position. [annotate]

    [annotate]

    cursor-active  cursor [Generic Function]
    (setf cursor-active)  value cursor [Generic Function]
              

    Returns (or sets) the "active" attribute of the cursor. When true, the cursor is active. [annotate]

    [annotate]

    cursor-state  cursor [Generic Function]
    (setf cursor-state)  value cursor [Generic Function]
              

    Returns (or sets) the "state" attribute of the cursor. When true, the cursor is visible. When false, the cursor is not visible. [annotate]

    [annotate]

    cursor-focus  cursor [Generic Function]
              

    Returns the "focus" attribute of the cursor. When true, the sheet owning the cursor has the input focus. [annotate]

    [annotate]

    cursor-visibility  cursor [Generic Function]
    (setf cursor-visibility)  visibility cursor [Generic Function]
              

    These are convenience functions that combine the functionality of cursor-active and cursor-state. The visibility can be either :on (meaning that the cursor is active and visible at its current position), :off (meaning that the cursor is active, but not visible at its current position), or nil (meaning that the cursor is not activate). [annotate]

    [annotate]

    15.3.2 Stream Text Cursor Protocol

    The following generic functions comprise the stream text cursor protocol. Any extended output stream class must implement methods for these generic functions. [annotate]

    stream-text-cursor  stream [Generic Function]
              

    [annotate]

    Note: [?] [edit]-- anon 2019-08-16 11:08Z
     

    (setf stream-text-cursor)  cursor stream [Generic Function]
              

    Returns (or sets) the text cursor object for the stream stream. [annotate]

    [annotate]

    stream-cursor-position  stream [Generic Function]
              

    Returns the current text cursor position for the extended output stream stream as two integer values, the x and y positions. [annotate]

    [annotate]

    (setf* stream-cursor-position)  x y stream [Generic Function]
              

    Sets the text cursor position of the extended output stream stream to x and y. x and y are in device units, and must be integers. [annotate]

    Note: So what is the coordinate system the stream cursor operates in? The sheet's device coordinate system? Why must these be integers? [edit]-- Gilbert Baumann 2004-06-09 22:11Z
     

    For CLIM implementations that do not support setf*, the "setter" function for this is stream-set-cursor-position. [annotate]

    [annotate]

    stream-increment-cursor-position  stream dx dy [Generic Function]
              

    Moves the text cursor position of the extended output stream stream relatively, adding dx to the x coordinate and dy to the y coordinate. Either of dx or dy may be nil, meaning the the x or y cursor position will be unaffected. Otherwise, dx and dy must be integers. [annotate]

    [annotate]