X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTField.java;h=681ce7e93e0cbf278dd0d6aaadd7a9e92c79c813;hb=6b2633acf2b801ac7353785746dd03603195e3ca;hp=992f274fb388792c1ed76c5e33bcc6a4472cb6a8;hpb=2b4274048c2f409b5eba8373ab3018aa75911c73;p=fanfix.git diff --git a/src/jexer/TField.java b/src/jexer/TField.java index 992f274..681ce7e 100644 --- a/src/jexer/TField.java +++ b/src/jexer/TField.java @@ -30,6 +30,7 @@ package jexer; import jexer.bits.CellAttributes; import jexer.bits.GraphicsChars; +import jexer.bits.StringUtils; import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; import static jexer.TKeypress.*; @@ -46,7 +47,7 @@ public class TField extends TWidget { /** * Background character for unfilled-in text. */ - protected char backgroundChar = GraphicsChars.HATCH; + protected int backgroundChar = GraphicsChars.HATCH; /** * Field text. @@ -64,6 +65,11 @@ public class TField extends TWidget { */ protected int position = 0; + /** + * Current editing position screen column number. + */ + protected int screenPosition = 0; + /** * Beginning of visible portion. */ @@ -193,10 +199,11 @@ public class TField extends TWidget { if ((mouseOnField()) && (mouse.isMouse1())) { // Move cursor int deltaX = mouse.getX() - getCursorX(); - position += deltaX; - if (position > text.length()) { - position = text.length(); + screenPosition += deltaX; + if (screenPosition > StringUtils.width(text)) { + screenPosition = StringUtils.width(text); } + position = screenToTextPosition(screenPosition); updateCursor(); return; } @@ -212,11 +219,18 @@ public class TField extends TWidget { if (keypress.equals(kbLeft)) { if (position > 0) { - position--; + if (position < codePointLength(text)) { + screenPosition -= StringUtils.width(text.codePointBefore(position)); + position -= Character.charCount(text.codePointBefore(position)); + } else { + screenPosition--; + position--; + } } if (fixed == false) { - if ((position == windowStart) && (windowStart > 0)) { - windowStart--; + if ((screenPosition == windowStart) && (windowStart > 0)) { + windowStart -= StringUtils.width(text.codePointAt( + screenToTextPosition(windowStart))); } } normalizeWindowStart(); @@ -224,15 +238,18 @@ public class TField extends TWidget { } if (keypress.equals(kbRight)) { - if (position < text.length()) { - position++; + if (position < codePointLength(text)) { + screenPosition += StringUtils.width(text.codePointAt(position)); + position += Character.charCount(text.codePointAt(position)); if (fixed == true) { - if (position == getWidth()) { - position--; + if (screenPosition == getWidth()) { + screenPosition--; + position -= Character.charCount(text.codePointAt(position)); } } else { - if ((position - windowStart) == getWidth()) { - windowStart++; + if ((screenPosition - windowStart) >= getWidth()) { + windowStart += StringUtils.width(text.codePointAt( + screenToTextPosition(windowStart))); } } } @@ -259,9 +276,10 @@ public class TField extends TWidget { } if (keypress.equals(kbDel)) { - if ((text.length() > 0) && (position < text.length())) { + if ((codePointLength(text) > 0) && (position < codePointLength(text))) { text = text.substring(0, position) + text.substring(position + 1); + screenPosition = StringUtils.width(text.substring(0, position)); } dispatch(false); return; @@ -269,15 +287,17 @@ public class TField extends TWidget { if (keypress.equals(kbBackspace) || keypress.equals(kbBackspaceDel)) { if (position > 0) { - position--; + position -= Character.charCount(text.codePointBefore(position)); text = text.substring(0, position) + text.substring(position + 1); + screenPosition = StringUtils.width(text.substring(0, position)); } if (fixed == false) { - if ((position == windowStart) + if ((screenPosition >= windowStart) && (windowStart > 0) ) { - windowStart--; + windowStart -= StringUtils.width(text.codePointAt( + screenToTextPosition(windowStart))); } } dispatch(false); @@ -290,27 +310,28 @@ public class TField extends TWidget { && !keypress.getKey().isCtrl() ) { // Plain old keystroke, process it - if ((position == text.length()) - && (text.length() < getWidth())) { + if ((position == codePointLength(text)) + && (StringUtils.width(text) < getWidth())) { // Append case appendChar(keypress.getKey().getChar()); - } else if ((position < text.length()) - && (text.length() < getWidth())) { + } else if ((position < codePointLength(text)) + && (StringUtils.width(text) < getWidth())) { // Overwrite or insert a character if (insertMode == false) { // Replace character text = text.substring(0, position) - + keypress.getKey().getChar() + + codePointString(keypress.getKey().getChar()) + text.substring(position + 1); - position++; + screenPosition += StringUtils.width(text.codePointAt(position)); + position += Character.charCount(keypress.getKey().getChar()); } else { // Insert character insertChar(keypress.getKey().getChar()); } - } else if ((position < text.length()) - && (text.length() >= getWidth())) { + } else if ((position < codePointLength(text)) + && (StringUtils.width(text) >= getWidth())) { // Multiple cases here if ((fixed == true) && (insertMode == true)) { @@ -318,19 +339,21 @@ public class TField extends TWidget { } else if ((fixed == true) && (insertMode == false)) { // Overwrite the last character, maybe move position text = text.substring(0, position) - + keypress.getKey().getChar() + + codePointString(keypress.getKey().getChar()) + text.substring(position + 1); - if (position < getWidth() - 1) { - position++; + if (screenPosition < getWidth() - 1) { + screenPosition += StringUtils.width(text.codePointAt(position)); + position += Character.charCount(keypress.getKey().getChar()); } } else if ((fixed == false) && (insertMode == false)) { // Overwrite the last character, definitely move position text = text.substring(0, position) - + keypress.getKey().getChar() + + codePointString(keypress.getKey().getChar()) + text.substring(position + 1); - position++; + screenPosition += StringUtils.width(text.codePointAt(position)); + position += Character.charCount(keypress.getKey().getChar()); } else { - if (position == text.length()) { + if (position == codePointLength(text)) { // Append this character appendChar(keypress.getKey().getChar()); } else { @@ -370,11 +393,12 @@ public class TField extends TWidget { } int end = windowStart + getWidth(); - if (end > text.length()) { - end = text.length(); + if (end > StringUtils.width(text)) { + end = StringUtils.width(text); } hLineXY(0, 0, getWidth(), backgroundChar, fieldColor); - putStringXY(0, 0, text.substring(windowStart, end), fieldColor); + putStringXY(0, 0, text.substring(screenToTextPosition(windowStart), + screenToTextPosition(end)), fieldColor); // Fix the cursor, it will be rendered by TApplication.drawAll(). updateCursor(); @@ -384,12 +408,34 @@ public class TField extends TWidget { // TField ----------------------------------------------------------------- // ------------------------------------------------------------------------ + /** + * Convert a char (codepoint) to a string. + * + * @param ch the char + * @return the string + */ + private String codePointString(final int ch) { + StringBuilder sb = new StringBuilder(1); + sb.append(Character.toChars(ch)); + return sb.toString(); + } + + /** + * Get the number of codepoints in a string. + * + * @param str the string + * @return the number of codepoints + */ + private int codePointLength(final String str) { + return str.codePointCount(0, str.length()); + } + /** * Get field background character. * * @return background character */ - public final char getBackgroundChar() { + public final int getBackgroundChar() { return backgroundChar; } @@ -398,7 +444,7 @@ public class TField extends TWidget { * * @param backgroundChar the background character */ - public void setBackgroundChar(final char backgroundChar) { + public void setBackgroundChar(final int backgroundChar) { this.backgroundChar = backgroundChar; } @@ -441,17 +487,40 @@ public class TField extends TWidget { } } + /** + * Determine string position from screen position. + * + * @param screenPosition the position on screen + * @return the equivalent position in text + */ + protected int screenToTextPosition(final int screenPosition) { + if (screenPosition == 0) { + return 0; + } + + int n = 0; + for (int i = 0; i < codePointLength(text); i++) { + n += StringUtils.width(text.codePointAt(i)); + if (n >= screenPosition) { + return i + 1; + } + } + // screenPosition exceeds the available text length. + throw new IndexOutOfBoundsException("screenPosition " + screenPosition + + " exceeds available text length " + codePointLength(text)); + } + /** * Update the visible cursor position to match the location of position * and windowStart. */ protected void updateCursor() { - if ((position > getWidth()) && fixed) { + if ((screenPosition > getWidth()) && fixed) { setCursorX(getWidth()); - } else if ((position - windowStart == getWidth()) && !fixed) { + } else if ((screenPosition - windowStart >= getWidth()) && !fixed) { setCursorX(getWidth() - 1); } else { - setCursorX(position - windowStart); + setCursorX(screenPosition - windowStart); } } @@ -464,7 +533,7 @@ public class TField extends TWidget { assert (windowStart == 0); return; } - windowStart = position - (getWidth() - 1); + windowStart = screenPosition - (getWidth() - 1); if (windowStart < 0) { windowStart = 0; } @@ -475,21 +544,23 @@ public class TField extends TWidget { /** * Append char to the end of the field. * - * @param ch = char to append + * @param ch char to append */ - protected void appendChar(final char ch) { + protected void appendChar(final int ch) { // Append the LAST character - text += ch; - position++; + text += codePointString(ch); + position += Character.charCount(ch); + screenPosition += StringUtils.width(ch); - assert (position == text.length()); + assert (position == codePointLength(text)); if (fixed) { - if (position == getWidth()) { - position--; + if (screenPosition >= getWidth()) { + position -= Character.charCount(ch); + screenPosition -= StringUtils.width(ch); } } else { - if ((position - windowStart) == getWidth()) { + if ((screenPosition - windowStart) >= getWidth()) { windowStart++; } } @@ -500,10 +571,12 @@ public class TField extends TWidget { * * @param ch char to append */ - protected void insertChar(final char ch) { - text = text.substring(0, position) + ch + text.substring(position); - position++; - if ((position - windowStart) == getWidth()) { + protected void insertChar(final int ch) { + text = text.substring(0, position) + codePointString(ch) + + text.substring(position); + position += Character.charCount(ch); + screenPosition += StringUtils.width(ch); + if ((screenPosition - windowStart) == getWidth()) { assert (!fixed); windowStart++; } @@ -515,6 +588,7 @@ public class TField extends TWidget { */ public void home() { position = 0; + screenPosition = 0; windowStart = 0; } @@ -523,13 +597,15 @@ public class TField extends TWidget { * adjust the window start to show as much of the field as possible. */ public void end() { - position = text.length(); + position = codePointLength(text); + screenPosition = StringUtils.width(text); if (fixed == true) { - if (position >= getWidth()) { - position = text.length() - 1; - } + if (screenPosition >= getWidth()) { + position = codePointLength(text) - 1; + screenPosition = StringUtils.width(text) - 1; + } } else { - windowStart = text.length() - getWidth() + 1; + windowStart = StringUtils.width(text) - getWidth() + 1; if (windowStart < 0) { windowStart = 0; } @@ -545,9 +621,9 @@ public class TField extends TWidget { * the available text */ public void setPosition(final int position) { - if ((position < 0) || (position >= text.length())) { + if ((position < 0) || (position >= codePointLength(text))) { throw new IndexOutOfBoundsException("Max length is " + - text.length() + ", requested position " + position); + codePointLength(text) + ", requested position " + position); } this.position = position; normalizeWindowStart();