• No results found

5.4 A prototype editor

5.4.2 Cursor movement

The lish editor has a cell cursor very similar to the one found in a spread- sheet. The user can move it around using the arrow keys, and commands given to the editor (either typed in the command window, or invoked via the shortcut keys) in general are applied at the current cursor position. One difference from the ordinary spreadsheet cursor is that in the lish, the cursor can be used to select whole sublists as well as individual cells. The user can drill into a selected sublist, taking the cursor inward one level, by pressing Enter, and come back up a level by pressing Backspace. This allows the user to choose at what granularity they wish to navigate the model.

The ragged text problem

In a word processor or text editor, cursor movement has to accommodate the possibility of “ragged” text. If the cursor is in a long line of text with a shorter line adjacent, then a strictly vertical movement of the cursor is not possible: there is no text at all in the target location for it to move to. A spreadsheet never has this problem, because the regular grid ensures there are no “void” spaces. The lish, however, has its own version of the ragged text problem. The user might want to navigate vertically across adjacent sublists of different widths, or horizontally across adjacent sublists of different heights. And the lish adds an extra complication: an adjacent sublist might be of insufficient depth to allow like-for-like positioning.

Text editors have adopted an almost universal behaviour for handling this problem. Suppose the cursor is in column 40 and the user presses the up arrow, but the line above is only 20 characters long. This upward movement is not prevented, and the cursor will simply be placed at the end of the shorter line – that being the closest it can get to strict vertical movement. However, the cursor will “remember” that it was in column 40 when the movement was initiated. If it is subsequently moved to another line that does have 40 or more characters, it will return to column 40. So the horizontal position is maintained as far as possible. A particularly important

feature is that if the user presses the up arrow followed by the down arrow, they will always get back to where they started. This contributes to avoiding premature commitment by making a navigational misstep readily reversible.

The shadow and residual cursor positions

The lish solution follows the spirit of the ordinary text editor, but requires the cursor to “remember” more than a single column position. In a lish, the cursor position is maintained internally as a pair of compound indices (sub- section 5.3.3) comprising its compound column and compound row indices. These indices always reference an actual location in the lish, and are where the cursor is drawn. In addition to these, the editor maintains two other pairs of indices.

First, there are the shadow compound indices. Their role is directly analogous to the column “memory” of the simple text editor above. When the cursor is initialised, and immediately after an edit, the shadow indices are set to be the same as the the actual indices. They can be thought of as giving a position where the cursor would “like” to be, but cannot attain in all cases if certain sublists are either not sufficiently long, or not sufficiently deep.

Second, there are the residual compound indices. Their role is to re- member the location of a nested item when the user has visited it, but then expressly drilled out again. Since the user has now signified that they do not want the cursor at the deeper level right now, the editor should make no attempt to place it there: this is the distinction from the shadow position above. But if the user does subsequently drill back in, the editor consults the residual compound indices to return the cursor to its previous position. These indices are specified relative to the shadow position, and are initially empty.

An example of moving the cursor

An example will make clearer the interplay between the actual, shadow and residual positions. Suppose the user has just edited cell Var2a in Figure 5.6. Following the procedure of subsection 5.3.3, we find that the cursor is now at compound indices ([2, 1], [1, 0]). After the edit, the shadow position is equal to the actual position, and the residual compound indices are empty: ([ ], [ ]).

outward movement, so the shadow follows the cursor and both arrive at compound indices ([2], [1, 0]). The cursor now selects the whole of the [•, Var2a, Var2b] sublist. The residual compound indices become ([1], [ ]), where the index of 1, that was dropped from the compound column index of the cursor upon drilling out, has now appeared in the residual. This index is retaining a memory of where to go should the user subsequently decide to drill back in.

Next, let us have the user press the up arrow. The cursor now needs to move to the single cell, “Title cell”, which is the only element available on the row above the current position. So the cursor position moves from ([2], [1, 0]) to ([ ], [0]). The shadow, on the other hand, is now set to the position where the cursor would have gone, had the structure on the row above matched that at its previous position: so the shadow position becomes ([2], [0, 0]). The residual position remains unchanged at ([1], [ ]) because the change in depth was entirely forced.

Now suppose the user retraces their previous step by pressing the down arrow. The first thing the editor does is to move the cursor directly down one row, from ([ ], [0]) to ([ ], [1]). If this were the end of the story, the cursor would now select the whole of the sublist forming the main table. But to finalise the new cursor position, the editor consults the shadow and “tops up” those indices where the structure was previously deficient to attain the shadow position, but has become newly available. Hence both the cursor and shadow positions arrive back at ([2], [1, 0]). Once again, no explicit change of level took place, so the residual position is unchanged at ([1], [ ]). Finally the user might drill back in by pressing Enter. Clearly this must select one of the cells in the currently selected sublist [•, Var2a, Var2b], but which one? If the residual compound coordinates were empty, the editor would simply select the first cell by default, and the cursor would go to ([2, 0], [1, 0]). But since a residual is available, it is consulted to fill in the new final index. Therefore the cursor is located at ([2, 1], [1, 0]), where it began this example, and the residual position becomes empty.

An en passant evaluation

Evaluating the “shadow” behaviour of the cursor was not an explicit goal of the user study (Chapter 8). But during that study, no participant had to ask, “How do I get to that cell?”, or even commented on the cursor behaviour at all. By appeal to the dog that did not bark, I conclude that the rather

complicated-looking behaviour specified above translates to something that is at least reasonably intuitive for the user.