This write-up details what could be considered a "full set" of standard core functionality for text input boxes in user interfaces for referential purposes during implementation.
The core components of textbox behaviour are split into three parts: One we can call Movements, which describe a means of moving from one position to the next (eg. one-character-to-the-left), Operations which use a movement to perform an action (eg. select-to) and finally Commmands, most of which will be an operation performing a movement, although they are not limited to this.
The caret/selection of a textbox is typically implemented as a pair of integers; both are grouped together as they are part of the same thing. The first integer (we'll refer to this as the head) is the caret's current position; the second integer (the tail) is the other end of the selection — in the case that there is no text selected the head and tail are the same value and only the caret is displayed. In the case that there is text selected the value of both sides differ from one another.
Textboxes support the following 3 core operations for navigating and editing text; each operation uses a single movement to achieve it's goal — in terms of implementation you could think of each operation implemented as a function which itself takes a movement-function as an argument. As the movements are common between operations this reduces code duplication as each movement only needs to be implemented once to be used by any operation.
The operations are as follows:
move-to
- Moves the position of the caret (both sides of the selection)select-to
- Moves only the head of the selection and leaves the tail uneffecteddelete-to
- Deletes everything between the caret and resultant positionSingle line movements are supported by both single-line textboxes and multi-line text boxes. The core single-line movements are as follows:
left
- move one character to the leftright
- move one character to the rightnext-word-end
- skip rightward whitespace and move to the end of the next word; if we're currently in the middle of a word move to the end of that word previous-word-end
- skip leftward whitespace and move to the start of the previous word; if we're currently in the middle of a word move to the start of that word start
- move to before the first character of the textend
- move to after the last character of the textIf by performing a movement it would result in the position being before the start of the text or after the end of the text, the result should be clamped to the text's bounds.
Multi-line movements are supported only by multi-line text boxes; these are as follows:
up
- move to the line abovedown
- move to the line belowline-start
- move to the start of the current line (the soft-line if word-wrapped)line-end
- move to the end of the current line (the soft-line if word-wrapped)page-up
- move up by the height of the textboxpage-down
- move down by the height of the textboxIn the case of several successive vertical movements the horizontal position should always be as close to the initial horizontal position from the first movement. Vertical movement horizontal positioning should be performed to keep visual consistency rather than character-offset consistency, eg. moving up a line should place the caret as close as possible on the screen to its original horizontal position.
In the case of page-up
and page-down
the caret should move the
distance of the textbox's height. A page-up movement followed by a page-down
movement should always result in returning to the original position, with the
exception of the case where the page-up movement places the caret at the
beginning of the text as there is not a whole textbox-height space above the
current position.
By combining the operations and movements described above, the following core commands can be supported:
backspace
- delete-to + left - (Backspace, Shift+Backspace)delete
- delete-to + right - (Delete)delete-word-left
- delete-to + word-left - (Ctrl+Backspace, Ctrl+Shift+Backspace)delete-word-right
- delete-to + word-right - (Ctrl+Delete)move-left
- move-to + left - (Left)move-right
- move-to + right - (Right)move-up
- move-to + up - (Up)move-down
- move-to + down - (Down)move-word-left
- move-to + word-left - (Ctrl+Left)move-word-right
- move-to + word-right - (Ctrl+Right)move-line-start
- move-to + line-start - (Home)move-line-end
- move-to + line-end - (End)move-start
- move-to + start - (Ctrl+Home)move-end
- move-to + end - (Ctrl+End)select-left
- select-to + left - (Shift+Left)select-right
- select-to + right - (Shift+Right)select-up
- select-to + up - (Shift+Up)select-down
- select-to + down - (Shift+Down)select-word-left
- select-to + word-left - (Ctrl+Shift+Left)select-word-right
- select-to + word-right - (Ctrl+Shift+Right)select-line-start
- select-to + line-start - (Shift+Home)select-line-end
- select-to + line-end - (Shift+End)select-start
- select-to + start - (Ctrl+Shift+Home)select-end
- select-to + end - (Ctrl+Shift+End)
Both the move-left
and move-right
commands have a special case:
if there is text selected, instead of performing the usual operation
of moving one character to the left/right, the caret is instead moved to the
corresponding edge of the selected text and the selection is canceled.
In addition to the movement based commands there are several other commands which do not rely on movement:
newline
- inserts a new line in a multi-line textbox - (Return, Shift+Return)select-all
- selects all text in the textbox - (Ctrl+A)cut
- copies the current selection to the clipboard and deletes it - (Ctrl+X, Shift+Delete)copy
- copies the current selection to the clipboard - (Ctrl+C, Ctrl+Insert)paste
- inserts the text currently on the clipboard - (Ctrl+V, Shift+Insert)undo
- undoes up to the last point of 300ms of inaction - (Ctrl+Z)redo
- negates a previously performed undo command - (Ctrl+Y)Textboxes typically support mouse input for modifying the selection. There are 3 modes to mouse input, the state of which is determined by the number of clicks that occurred:
single-click
- character-granularitydouble-click
- word-granularitytriple-click
- line-granularityWhen the mouse is pressed down the both sides of the selection are set to the current mouse cursor position. If the mouse is then dragged the head of the selection is moved to that position while the tail remains in place of the original click. If Shift is held when the mouse is clicked the tail of the selection is uneffected and only the head is changed.
The placement of both sides of the selection are based around the granularities specified above, such that, for example, if one were to double click and drag their mouse it would select a word at a time; if one were to triple-click it would select a line at a time.