NAME
Term::Fabulous::Widget::TextInput - Common base class of the text input widgets
SYNOPSIS
use Clay::UI::Enum::Result;
use Term::Fabulous::Widget::TextField;
# The parameters and methods below work the same for both text inputs:
my $field = Term::Fabulous::Widget::TextField->new(
value => 'initial text',
placeholder => 'Type here',
max_length => 40,
accept => 'a-zA-Z ', # the characters the user may type
read_only => 0,
placeholder_color => '#787e8a',
selection_color => [ 38, 79, 120 ],
);
my $text = $field->value; # a character string
$field->value('replaced'); # fires no Change event
$field->on( Change => sub ($event) {
say 'now: ', $event->value;
return Clay::UI::Enum::Result->CONTINUE;
} );
DESCRIPTION
Term::Fabulous::Widget::TextInput is the abstract base class of Term::Fabulous::Widget::TextField (one line) and Term::Fabulous::Widget::TextArea (several lines). It holds what both have in common: the text with its cursor, selection, undo history and clipboard (kept in a Term::Fabulous::Editor), the editing keys, mouse selection, the placeholder and the read_only mode. You do not create a TextInput directly; its new dies.
The text is a Perl character string (decoded text), not UTF-8 encoded bytes. The cursor moves by grapheme clusters, that is by what a reader sees as one character (a letter with a combining accent, an emoji with modifiers, a flag), and wide characters such as CJK take two columns.
While the input has the focus, its content is painted on focus_background_color and the cursor is shown as a block: the character under it in reverse video. Selected text is painted on selection_color. While the text is empty, the placeholder is shown instead, in placeholder_color.
Everything described for Term::Fabulous::Widget::Input applies as well: disabled, the colors, focus, sizing and the Change event.
CONSTRUCTOR
new
my $field = Term::Fabulous::Widget::TextField->new(%parameters);
my $area = Term::Fabulous::Widget::TextArea->new(%parameters);
The text inputs accept the parameters of "CONSTRUCTOR" in Term::Fabulous::Widget::Input and these. Unknown parameters die.
value-
A character string. Default:
''. The initial text. The cursor starts at its end. A text field turns line breaks into spaces; a text area converts"\r\n"and"\r"to"\n". Dies if the text is longer thanmax_length. placeholder-
A character string. Default:
''(none). A hint shown inplaceholder_colorwhile the text is empty. It is never part of thevalue. max_length-
A non-negative integer, or
undef. Default:undef(no limit). The most characters the text may hold, counted in grapheme clusters; in a text area every line break counts as one. Typing and pasting stop at the limit: pasted text is cut to fit. Dies if the initialvalueis longer. accept-
Which characters the user may type or paste. One of:
a string: the body of a character class, written as between the brackets of
[...]in a regular expression.'0-9'accepts digits,'a-zA-Z 'letters and blanks,'^0-9'everything but digits. A-that is not part of a range goes first or last ('0-9-'), and]and\are written\]and\\;a regular expression, matched against each character (strictly, each grapheme cluster: a letter with its accents, an emoji with its modifiers):
qr/\p{L}/accepts letters of every script;a code reference, called with each grapheme cluster, that returns true to accept it:
sub ($cluster) { $cluster ne ' ' };undef, the default: what thevalidatorsuggests, if anything (integersuggests'0-9-', see "accept" in Term::Fabulous::Validator), else every character.
Typing a rejected character does nothing. Pasted text keeps its accepted characters and drops the rest; when none is accepted, the paste does nothing, and does not replace the selection either. Line breaks in a text area are always accepted. Dies if the initial
valuehas a rejected character. To let the user type every character into a field whose validator suggests a restriction, giveaccept => qr/./. See "accept". read_only-
A boolean, stored as 1 or 0. Default: 0. A read-only input can still take the focus, and its text can be selected and copied, but the user cannot change it: typing and the editing keys are not used and bubble on to the ancestors. Programmatic writes to
valuestill work. A read-only input looks like an editable one; disable it ("disabled" in Term::Fabulous::Widget::Input) when the user should see that the text cannot be changed. A reference dies. placeholder_color-
A color, in any format Term::Fabulous::Widget::Input accepts. Default: the theme's
text_input.placeholder,[120, 126, 138, 255]in the dark theme, a gray. selection_color-
A color, in any format Term::Fabulous::Widget::Input accepts. The background of selected text. Default: the theme's
text_input.selection,[38, 79, 120, 255]in the dark theme, a dark blue. background_color-
Any Term::Fabulous::Color format, stored as
[r, g, b, a]. Default: the theme'stext_input.background,[36, 40, 48, 255]in the dark theme, a dark gray, so the input stands out from its surroundings. Pass[0, 0, 0, 0]for no background of its own.
METHODS
value
my $text = $input->value;
$input->value("new text");
Accessor for the text, a character string. Writing replaces the whole text, puts the cursor at its end, clears the selection and the undo history, marks the input changed, and returns the new text (after line-break conversion). It fires no Change event. Dies if the new text is not a string or is longer than max_length.
max_length
my $limit = $input->max_length;
$input->max_length(10);
$input->max_length(undef); # no limit
Accessor for the length limit (see the max_length parameter). Returns the new limit. Dies if the limit is not a non-negative integer or undef, or if the current text is already longer; the limit then stays as it was.
accept
my $spec = $input->accept; # as given; undef means the validator's suggestion
$input->accept('0-9');
$input->accept(qr/[^\s]/);
$input->accept(undef);
Accessor for the accept spec (see the accept parameter). The reader returns the spec as it was given, undef included: what the input actually uses then is the validator's suggestion, or nothing. Writing undef goes back to the validator's suggestion; writing qr/./ accepts every character, whatever the validator suggests. Writing returns the new spec. Dies, and keeps the old spec, for a string that is not a valid character class body, for a reference of another kind, and when the current text has a character the new spec rejects. Setting value to a text with a rejected character dies too: the restriction is for the user, the program is expected to know better.
placeholder
$input->placeholder('Search');
Accessor for the placeholder text. Writing marks the input changed and returns the new placeholder; a value that is not a string dies and leaves the placeholder unchanged.
read_only
my $is_read_only = $input->read_only;
$input->read_only(1);
Accessor for the read_only flag. Returns 1 or 0, also for a value passed to new. Any plain value is accepted as a boolean; a reference dies and leaves the flag unchanged. Writing does not change what the input shows.
placeholder_color
$input->placeholder_color('#888888');
Accessor for the placeholder color. Writing marks the input changed and returns the new color as [r, g, b, a]; an invalid color dies and leaves the color unchanged.
selection_color
$input->selection_color([ 60, 60, 120 ]);
Accessor for the selection background. Writing marks the input changed and returns the new color as [r, g, b, a]; an invalid color dies and leaves the color unchanged.
editor
my $editor = $input->editor;
The Term::Fabulous::Editor that holds the text, the cursor, the selection and the undo history. Use it to move the cursor, select or edit text from your program. Afterwards call $input->mark_changed so that a frame is drawn: the input notices the change of the editor ("revision" in Term::Fabulous::Editor) when the frame is drawn, scrolls the cursor into view and paints the text. Edits made through the editor fire no Change event.
$field->editor->select_all;
$field->mark_changed;
$area->editor->move_document_start;
$area->editor->insert("Dear Sir or Madam,\n");
$area->mark_changed;
KEYS
The keys below are named as "main_key_name" in Term::Fabulous::Event::KeyPress returns them, so the keypad keys a terminal with the kitty keyboard protocol tells apart work as their main keyboard keys. A text input uses them while it has the focus and is enabled; they then do not bubble. All other keys bubble on to the ancestors: for example Escape, Tab, BackTab (Shift+Tab), F1 to F12 and Alt+ combinations. Term::Fabulous moves the focus on Tab and BackTab and stops on Ctrl+C, after the key has been delivered.
- Typing
-
A printable character (pressed without
CtrlorAlt) is inserted at the cursor, replacing the selection. Left,Right-
Move the cursor one character left or right, across line breaks. With a selection, they move to its start (
Left) or end (Right) and clear it. Ctrl+Left,Ctrl+Right-
Move to the start of the word before the cursor, or to the end of the word after it. Words are runs of letters, digits and
_. Home,End-
Move to the start or end of the line (in a text area: of the text line, not of the wrapped row).
Ctrl+Home,Ctrl+End-
Move to the start or end of the whole text.
Shift+Left,Shift+Right,Ctrl+Shift+Left,Ctrl+Shift+Right,Shift+Home,Shift+End,Ctrl+Shift+Home,Ctrl+Shift+End-
The movements above, extending the selection.
Ctrl+A-
Selects the whole text.
Backspace,Delete-
Delete the selection, or else the character before (
Backspace) or after (Delete) the cursor. Ctrl+W,Ctrl+Delete-
Delete the selection, or else the word before (
Ctrl+W) or after (Ctrl+Delete) the cursor. Ctrl+U,Ctrl+K-
Delete the selection, or else everything from the start of the line to the cursor (
Ctrl+U) or from the cursor to the end of the line (Ctrl+K). At the very start (end) of a line, they delete the line break before (after) it. Ctrl+X,Shift+Delete-
Cut: copy the selection to the clipboard and delete it.
Ctrl+Insert-
Copy the selection to the clipboard.
Ctrl+Cis not copy: it stops Term::Fabulous (unless the UI was made withstop_on_ctrl_c => 0; the input does not useCtrl+Ceither way, so a program can bind it). Ctrl+V,Shift+Insert-
Paste the clipboard at the cursor, replacing the selection.
Ctrl+Z,Ctrl+Y-
Undo and redo. Consecutive typing is undone one word (or one run of spaces) at a time; up to 100 steps are kept.
The clipboard is the one of "clipboard" in Term::Fabulous::Editor: one string shared by all text inputs of the program. It is not the system clipboard.
While read_only is set, typing and the keys that change the text (Backspace, Delete, Ctrl+W, Ctrl+Delete, Ctrl+U, Ctrl+K, Ctrl+X, Shift+Delete, Ctrl+V, Shift+Insert, Ctrl+Z, Ctrl+Y) are not used and bubble; movement, selection and copying still work.
While the text is hidden (a Term::Fabulous::Widget::TextField with a mask), the word keys act on the whole text, so they cannot tell where its spaces are: Ctrl+Left and Ctrl+Right move like Home and End, Ctrl+W and Ctrl+Delete delete like Ctrl+U and Ctrl+K. Ctrl+X, Shift+Delete and Ctrl+Insert do nothing and bubble: hidden text is not copied to the clipboard.
Term::Fabulous::Widget::TextField and Term::Fabulous::Widget::TextArea add keys of their own (Enter, Up, Down, ...); see their KEYS sections.
MOUSE
- Click
-
A left click places the cursor at the clicked character and focuses the input.
- Shift+click
-
A left click with
Shiftheld extends the selection from the cursor to the clicked character. Many terminals keepShiftwith the mouse for their own text selection and do not pass such a click on; dragging works everywhere. - Double click
-
A second left click at the same position within 0.4 seconds selects the word there (or the single character, when it is not part of a word), or the whole text while it is hidden.
- Drag
-
Moving the pointer with the left button held selects from where the button went down to the pointer, as long as the pointer stays over the input.
EVENTS
Change-
Term::Fabulous::Event::Change after every change the user makes to the text (typing, deleting, cutting, pasting, undo, redo), with the new text as its
value. Keys that do not change the text (cursor movement, copying, typing atmax_length) fire nothing. Programmatic changes throughvalueoreditorfire nothing.
Term::Fabulous::Widget::TextField also fires Term::Fabulous::Event::Submit on Enter.
KDL PROPERTIES
The properties of "KDL PROPERTIES" in Term::Fabulous::Widget::Input, plus value, placeholder, max_length, accept (a character class body), read_only (#true / #false), placeholder_color and selection_color. max_length, accept and validator are applied before value, wherever they stand, so a value that is too long or has a rejected character dies.
TextField "nick" {
max_length 12
accept "a-zA-Z0-9_"
value "guest"
placeholder "Nickname"
}
TextField "port" {
validator "integer"
required #true
}
SUBCLASS INTERFACE
Term::Fabulous::Widget::TextField and Term::Fabulous::Widget::TextArea build on these; a new kind of text input would too. A text input lays its text out with a Term::Fabulous::TextView ("view"), which does the wrapping, the scrolling and the mapping between cells and text positions; the text input keeps the keys, the mouse, the painting and the placeholder.
is_multi_line
method is_multi_line :common () { return 1 }
Class method. Whether the editor keeps line breaks (1) or turns them into spaces (0, the default).
view
$self->view->set_wrap(1);
The Term::Fabulous::TextView of the input: one row without wrapping until a subclass changes its settings (see "set_wrap, set_scrollbar" in Term::Fabulous::TextView). The input gives it the size of its buffer and lets it follow the cursor every time a frame is drawn, and paints the rows it shows. Use it to move the cursor by rows ("move_vertically" in Term::Fabulous::TextView) or to scroll ("scroll_rows" in Term::Fabulous::TextView); call mark_changed after changing what it shows.
natural_size
method natural_size () { return ( $preferred_columns, 1 ) }
Required; see "natural_size" in Term::Fabulous::Widget::Input.
paint
method paint :override () {
$self->SUPER::paint;
... # paint more, for example a scrollbar
}
Paints the rows the view shows, with the selection and the cursor, or the placeholder while the text is empty; see "paint" in Term::Fabulous::Widget::Input. Override it to paint more.
hides_text
method hides_text :override () { return defined $mask ? 1 : 0 }
Whether the text is not shown as it is. Default: 0; the text field returns 1 while it has a mask. While it is true, the text cannot be copied or cut, and the word keys and the double click act on the whole text (see "KEYS").
display_cluster
method display_cluster :override ($cluster) { return $shown }
How one grapheme cluster of the text is shown; the view lays the text out with what this returns. The default replaces control characters (see "sanitize_text" in Term::Fabulous::Unicode); the text field returns its mask instead when one is set. When what it returns changes, call "display_changed" in Term::Fabulous::TextView.
apply_edit
return $self->apply_edit( $self->editor->insert($text) );
Call after an editor edit made on behalf of the user: marks the input changed (the next frame scrolls to the cursor and paints the result), and fires Change when the argument is true (the editor's edit methods return whether the text changed). Returns 1, so it can be returned from handle_key directly.
SEE ALSO
Term::Fabulous::Widget::TextField, Term::Fabulous::Widget::TextArea, Term::Fabulous::Editor, Term::Fabulous::TextView, Term::Fabulous::Widget::Input, the editing section of the forms guide, "Copy and paste through the clipboard" in Term::Fabulous::Cookbook::Forms.