2 * Jexer - Java Text User Interface
4 * License: LGPLv3 or later
6 * This module is licensed under the GNU Lesser General Public License
7 * Version 3. Please see the file "COPYING" in this directory for more
8 * information about the GNU Lesser General Public License Version 3.
10 * Copyright (C) 2015 Kevin Lamonte
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 3 of
15 * the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this program; if not, see
24 * http://www.gnu.org/licenses/, or write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
33 import jexer
.bits
.CellAttributes
;
34 import jexer
.bits
.GraphicsChars
;
35 import jexer
.event
.TKeypressEvent
;
36 import jexer
.event
.TMouseEvent
;
37 import static jexer
.TKeypress
.*;
40 * TField implements an editable text field.
42 public class TField
extends TWidget
{
47 protected String text
= "";
54 public final String
getText() {
61 * @param text the new field text
63 public final void setText(String text
) {
70 * If true, only allow enough characters that will fit in the width. If
71 * false, allow the field to scroll to the right.
73 protected boolean fixed
= false;
76 * Current editing position within text.
78 protected int position
= 0;
81 * Beginning of visible portion.
83 protected int windowStart
= 0;
86 * If true, new characters are inserted at position.
88 protected boolean insertMode
= true;
91 * Remember mouse state.
93 protected TMouseEvent mouse
;
96 * The action to perform when the user presses enter.
98 protected TAction enterAction
;
101 * The action to perform when the text is updated.
103 protected TAction updateAction
;
106 * Public constructor.
108 * @param parent parent widget
109 * @param x column relative to parent
110 * @param y row relative to parent
111 * @param width visible text width
112 * @param fixed if true, the text cannot exceed the display width
114 public TField(final TWidget parent
, final int x
, final int y
,
115 final int width
, final boolean fixed
) {
117 this(parent
, x
, y
, width
, fixed
, "", null, null);
121 * Public constructor.
123 * @param parent parent widget
124 * @param x column relative to parent
125 * @param y row relative to parent
126 * @param width visible text width
127 * @param fixed if true, the text cannot exceed the display width
128 * @param text initial text, default is empty string
130 public TField(final TWidget parent
, final int x
, final int y
,
131 final int width
, final boolean fixed
, final String text
) {
133 this(parent
, x
, y
, width
, fixed
, text
, null, null);
137 * Public constructor.
139 * @param parent parent widget
140 * @param x column relative to parent
141 * @param y row relative to parent
142 * @param width visible text width
143 * @param fixed if true, the text cannot exceed the display width
144 * @param text initial text, default is empty string
145 * @param enterAction function to call when enter key is pressed
146 * @param updateAction function to call when the text is updated
148 public TField(final TWidget parent
, final int x
, final int y
,
149 final int width
, final boolean fixed
, final String text
,
150 final TAction enterAction
, final TAction updateAction
) {
152 // Set parent and window
153 super(parent
, x
, y
, width
, 1);
155 setCursorVisible(true);
158 this.enterAction
= enterAction
;
159 this.updateAction
= updateAction
;
163 * Returns true if the mouse is currently on the field.
165 * @return if true the mouse is currently on the field
167 protected boolean mouseOnField() {
168 int rightEdge
= getWidth() - 1;
170 && (mouse
.getY() == 0)
171 && (mouse
.getX() >= 0)
172 && (mouse
.getX() <= rightEdge
)
180 * Dispatch to the action function.
182 * @param enter if true, the user pressed Enter, else this was an update
185 protected void dispatch(final boolean enter
) {
187 if (enterAction
!= null) {
191 if (updateAction
!= null) {
198 * Draw the text field.
202 CellAttributes fieldColor
;
204 if (isAbsoluteActive()) {
205 fieldColor
= getTheme().getColor("tfield.active");
207 fieldColor
= getTheme().getColor("tfield.inactive");
210 int end
= windowStart
+ getWidth();
211 if (end
> text
.length()) {
214 getScreen().hLineXY(0, 0, getWidth(), GraphicsChars
.HATCH
, fieldColor
);
215 getScreen().putStringXY(0, 0, text
.substring(windowStart
, end
),
218 // Fix the cursor, it will be rendered by TApplication.drawAll().
223 * Update the cursor position.
225 protected void updateCursor() {
226 if ((position
> getWidth()) && fixed
) {
227 setCursorX(getWidth());
228 } else if ((position
- windowStart
== getWidth()) && !fixed
) {
229 setCursorX(getWidth() - 1);
231 setCursorX(position
- windowStart
);
236 * Handle mouse button presses.
238 * @param mouse mouse button event
241 public void onMouseDown(final TMouseEvent mouse
) {
244 if ((mouseOnField()) && (mouse
.isMouse1())) {
246 int deltaX
= mouse
.getX() - getCursorX();
248 if (position
> text
.length()) {
249 position
= text
.length();
259 * @param keypress keystroke event
262 public void onKeypress(final TKeypressEvent keypress
) {
264 if (keypress
.equals(kbLeft
)) {
268 if (fixed
== false) {
269 if ((position
== windowStart
) && (windowStart
> 0)) {
276 if (keypress
.equals(kbRight
)) {
277 if (position
< text
.length()) {
280 if (position
== getWidth()) {
284 if ((position
- windowStart
) == getWidth()) {
292 if (keypress
.equals(kbEnter
)) {
297 if (keypress
.equals(kbIns
)) {
298 insertMode
= !insertMode
;
301 if (keypress
.equals(kbHome
)) {
307 if (keypress
.equals(kbEnd
)) {
308 position
= text
.length();
310 if (position
>= getWidth()) {
311 position
= text
.length() - 1;
314 windowStart
= text
.length() - getWidth() + 1;
315 if (windowStart
< 0) {
322 if (keypress
.equals(kbDel
)) {
323 if ((text
.length() > 0) && (position
< text
.length())) {
324 text
= text
.substring(0, position
)
325 + text
.substring(position
+ 1);
330 if (keypress
.equals(kbBackspace
) || keypress
.equals(kbBackspaceDel
)) {
333 text
= text
.substring(0, position
)
334 + text
.substring(position
+ 1);
336 if (fixed
== false) {
337 if ((position
== windowStart
)
347 if (!keypress
.getKey().isFnKey()
348 && !keypress
.getKey().isAlt()
349 && !keypress
.getKey().isCtrl()
351 // Plain old keystroke, process it
352 if ((position
== text
.length())
353 && (text
.length() < getWidth())) {
356 appendChar(keypress
.getKey().getChar());
357 } else if ((position
< text
.length())
358 && (text
.length() < getWidth())) {
360 // Overwrite or insert a character
361 if (insertMode
== false) {
363 text
= text
.substring(0, position
)
364 + keypress
.getKey().getChar()
365 + text
.substring(position
+ 1);
369 insertChar(keypress
.getKey().getChar());
371 } else if ((position
< text
.length())
372 && (text
.length() >= getWidth())) {
374 // Multiple cases here
375 if ((fixed
== true) && (insertMode
== true)) {
376 // Buffer is full, do nothing
377 } else if ((fixed
== true) && (insertMode
== false)) {
378 // Overwrite the last character, maybe move position
379 text
= text
.substring(0, position
)
380 + keypress
.getKey().getChar()
381 + text
.substring(position
+ 1);
382 if (position
< getWidth() - 1) {
385 } else if ((fixed
== false) && (insertMode
== false)) {
386 // Overwrite the last character, definitely move position
387 text
= text
.substring(0, position
)
388 + keypress
.getKey().getChar()
389 + text
.substring(position
+ 1);
392 if (position
== text
.length()) {
393 // Append this character
394 appendChar(keypress
.getKey().getChar());
396 // Insert this character
397 insertChar(keypress
.getKey().getChar());
403 // Append this character
404 appendChar(keypress
.getKey().getChar());
410 // Pass to parent for the things we don't care about.
411 super.onKeypress(keypress
);
415 * Append char to the end of the field.
417 * @param ch = char to append
419 protected void appendChar(final char ch
) {
420 // Append the LAST character
424 assert (position
== text
.length());
427 if (position
== getWidth()) {
431 if ((position
- windowStart
) == getWidth()) {
438 * Insert char somewhere in the middle of the field.
440 * @param ch char to append
442 protected void insertChar(final char ch
) {
443 text
= text
.substring(0, position
) + ch
+ text
.substring(position
);
445 if ((position
- windowStart
) == getWidth()) {