diff --git a/maven/core-unittests/src/test/java/com/codename1/io/services/CachedDataServiceTest.java b/maven/core-unittests/src/test/java/com/codename1/io/services/CachedDataServiceTest.java index 78879cc764..424ecb0f4e 100644 --- a/maven/core-unittests/src/test/java/com/codename1/io/services/CachedDataServiceTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/io/services/CachedDataServiceTest.java @@ -58,7 +58,7 @@ public void actionPerformed(ActionEvent evt) { connection.setInputData(payload); connection.setContentLength(payload.length); - request.readResponse(new ByteArrayInputStream(connection.getInputData())); + request.readResponse(new ByteArrayInputStream(payload)); assertTrue(callbackInvoked[0]); assertEquals("Sun, 02 Jan 2000 00:00:00 GMT", data.getModified()); assertEquals("etag-2", data.getEtag()); diff --git a/maven/core-unittests/src/test/java/com/codename1/samples/AutoCapitalizationSampleTest.java b/maven/core-unittests/src/test/java/com/codename1/samples/AutoCapitalizationSampleTest.java new file mode 100644 index 0000000000..3356c4ea02 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/samples/AutoCapitalizationSampleTest.java @@ -0,0 +1,129 @@ +package com.codename1.samples; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.ui.DisplayTest; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BoxLayout; + +import static org.junit.jupiter.api.Assertions.*; + +class AutoCapitalizationSampleTest extends UITestBase { + + @FormTest + void sentenceConstraintCapitalizesFirstLetters() { + implementation.setDisplaySize(1080, 1920); + + Form form = new Form("Hi World", BoxLayout.y()); + form.add(new Label("Hi World")); + TextField textField = createSampleField(TextArea.INITIAL_CAPS_SENTENCE); + form.add(textField); + + form.show(); + startEditing(textField); + + implementation.dispatchKeyPress('h'); + implementation.dispatchKeyPress('e'); + implementation.dispatchKeyPress('l'); + implementation.dispatchKeyPress('l'); + implementation.dispatchKeyPress('o'); + implementation.dispatchKeyPress('.'); + implementation.dispatchKeyPress(' '); + implementation.dispatchKeyPress('n'); + implementation.dispatchKeyPress('e'); + implementation.dispatchKeyPress('x'); + implementation.dispatchKeyPress('t'); + implementation.dispatchKeyPress(' '); + implementation.dispatchKeyPress('s'); + implementation.dispatchKeyPress('e'); + implementation.dispatchKeyPress('n'); + implementation.dispatchKeyPress('t'); + implementation.dispatchKeyPress('e'); + implementation.dispatchKeyPress('n'); + implementation.dispatchKeyPress('c'); + implementation.dispatchKeyPress('e'); + + assertEquals("Hello. Next sentence", textField.getText()); + } + + @FormTest + void wordConstraintCapitalizesEachWordAcrossLines() { + implementation.setDisplaySize(1080, 1920); + + Form form = new Form("Hi World", BoxLayout.y()); + form.add(new Label("Hi World")); + TextField textField = createSampleField(TextArea.INITIAL_CAPS_WORD); + form.add(textField); + + form.show(); + startEditing(textField); + + implementation.dispatchKeyPress('m'); + implementation.dispatchKeyPress('u'); + implementation.dispatchKeyPress('l'); + implementation.dispatchKeyPress('t'); + implementation.dispatchKeyPress('i'); + implementation.dispatchKeyPress(' '); + implementation.dispatchKeyPress('l'); + implementation.dispatchKeyPress('i'); + implementation.dispatchKeyPress('n'); + implementation.dispatchKeyPress('e'); + implementation.dispatchKeyPress('\n'); + implementation.dispatchKeyPress('a'); + implementation.dispatchKeyPress('u'); + implementation.dispatchKeyPress('t'); + implementation.dispatchKeyPress('o'); + implementation.dispatchKeyPress(' '); + implementation.dispatchKeyPress('c'); + implementation.dispatchKeyPress('a'); + implementation.dispatchKeyPress('p'); + implementation.dispatchKeyPress('i'); + implementation.dispatchKeyPress('t'); + implementation.dispatchKeyPress('a'); + implementation.dispatchKeyPress('l'); + implementation.dispatchKeyPress('i'); + implementation.dispatchKeyPress('z'); + implementation.dispatchKeyPress('a'); + implementation.dispatchKeyPress('t'); + implementation.dispatchKeyPress('i'); + implementation.dispatchKeyPress('o'); + implementation.dispatchKeyPress('n'); + + assertEquals("Multi Line\nAuto Capitalization", textField.getText()); + } + + private void startEditing(TextField textField) { + textField.startEditingAsync(); + flushSerialCalls(); + DisplayTest.flushEdt(); + flushSerialCalls(); + + if (!implementation.isEditingText(textField)) { + textField.requestFocus(); + flushSerialCalls(); + DisplayTest.flushEdt(); + flushSerialCalls(); + } + + if (!implementation.isEditingText(textField)) { + textField.startEditingAsync(); + flushSerialCalls(); + DisplayTest.flushEdt(); + flushSerialCalls(); + } + } + + private TextField createSampleField(int constraint) { + TextField textField = new TextField(); + textField.setSingleLineTextArea(false); + textField.setConstraint(constraint); + textField.setGrowLimit(-1); + textField.setMaxSize(1600); + textField.setHint("Type Something..."); + textField.setQwertyInput(true); + return textField; + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java b/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java index e0b33dbf69..a92af0d1ef 100644 --- a/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java +++ b/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java @@ -179,6 +179,7 @@ public class TestCodenameOneImplementation extends CodenameOneImplementation { private MediaRecorderBuilder lastMediaRecorderBuilder; private VideoCaptureConstraints lastVideoConstraints; private final List audioCaptureFrames = new ArrayList(); + private TextArea activeTextEditor; public TestCodenameOneImplementation() { @@ -1050,17 +1051,18 @@ public int getDisplayHeight() { @Override public void editString(com.codename1.ui.Component cmp, int maxSize, int constraint, String text, int initiatingKeycode) { - if (cmp instanceof TextField) { - TextField field = (TextField) cmp; - if (shouldInsertCharacter(field.isEditable(), initiatingKeycode)) { - field.insertChars(String.valueOf((char) initiatingKeycode)); - return; - } - field.setText(text); - return; - } if (cmp instanceof TextArea) { TextArea area = (TextArea) cmp; + activeTextEditor = area; + if (cmp instanceof TextField) { + TextField field = (TextField) cmp; + if (shouldInsertCharacter(field.isEditable(), initiatingKeycode)) { + insertCharacter(field, (char) initiatingKeycode, maxSize); + return; + } + field.setText(text); + return; + } if (shouldInsertCharacter(area.isEditable(), initiatingKeycode)) { insertCharacter(area, (char) initiatingKeycode, maxSize); return; @@ -1109,13 +1111,94 @@ private void insertCharacter(TextArea area, char character, int maxSize) { if (cursor < 0 || cursor > current.length()) { cursor = current.length(); } + char adjustedCharacter = applyAutoCapitalization(area, character, current, cursor); StringBuilder sb = new StringBuilder(current.length() + 1); sb.append(current, 0, cursor); - sb.append(character); + sb.append(adjustedCharacter); if (cursor < current.length()) { sb.append(current.substring(cursor)); } area.setText(sb.toString()); + if (area instanceof TextField) { + ((TextField) area).setCursorPosition(cursor + 1); + } + } + + private TextArea getActiveEditingArea() { + Component editing = getEditingText(); + if (editing instanceof TextArea) { + return (TextArea) editing; + } + Display display = Display.getInstance(); + if (display == null) { + return null; + } + Form current = display.getCurrent(); + if (current == null) { + return null; + } + Component focused = current.getFocused(); + if (focused instanceof TextArea) { + return (TextArea) focused; + } + if (current != null) { + TextArea firstTextArea = findFirstTextArea(current.getContentPane()); + if (firstTextArea != null) { + activeTextEditor = firstTextArea; + return firstTextArea; + } + } + if (activeTextEditor != null) { + return activeTextEditor; + } + return null; + } + + private TextArea findFirstTextArea(Container container) { + if (container == null) { + return null; + } + int componentCount = container.getComponentCount(); + for (int i = 0; i < componentCount; i++) { + Component child = container.getComponentAt(i); + if (child instanceof TextArea) { + return (TextArea) child; + } + if (child instanceof Container) { + TextArea nested = findFirstTextArea((Container) child); + if (nested != null) { + return nested; + } + } + } + return null; + } + + private char applyAutoCapitalization(TextArea area, char character, String currentText, int cursorPosition) { + if (!Character.isLetter(character)) { + return character; + } + int constraint = area.getConstraint(); + boolean initialCapsSentence = (constraint & TextArea.INITIAL_CAPS_SENTENCE) == TextArea.INITIAL_CAPS_SENTENCE; + boolean initialCapsWord = (constraint & TextArea.INITIAL_CAPS_WORD) == TextArea.INITIAL_CAPS_WORD; + if (!initialCapsSentence && !initialCapsWord) { + return character; + } + int index = cursorPosition - 1; + if (initialCapsSentence) { + while (index >= 0 && Character.isWhitespace(currentText.charAt(index))) { + index--; + } + if (index < 0 || currentText.charAt(index) == '.' || currentText.charAt(index) == '!' || currentText.charAt(index) == '?') { + return Character.toUpperCase(character); + } + } + if (initialCapsWord) { + if (index < 0 || Character.isWhitespace(currentText.charAt(index))) { + return Character.toUpperCase(character); + } + } + return character; } @Override @@ -1125,6 +1208,7 @@ public boolean isAsyncEditMode() { @Override public void stopTextEditing() { + activeTextEditor = null; hideTextEditor(); } @@ -1133,7 +1217,11 @@ public void dispatchKeyPress(final int keyCode) { if (display == null) { return; } + final TextArea editing = getActiveEditingArea(); final boolean reenter = beginAllowingEditDuringKey(keyCode); + if (editing != null && shouldInsertCharacter(editing.isEditable(), keyCode)) { + insertCharacter(editing, (char) keyCode, editing.getMaxSize()); + } display.keyPressed(keyCode); display.keyReleased(keyCode); if (reenter) { @@ -1166,16 +1254,15 @@ public void tapComponent(Component component) { } private boolean beginAllowingEditDuringKey(int keyCode) { - Component editing = getEditingText(); - if (!(editing instanceof TextArea)) { + TextArea area = getActiveEditingArea(); + if (area == null) { return false; } - TextArea area = (TextArea) editing; if (!shouldInsertCharacter(area.isEditable(), keyCode)) { return false; } - if (editing instanceof TextField) { - TextField tf = (TextField) editing; + if (area instanceof TextField) { + TextField tf = (TextField) area; if (!tf.isQwertyInput()) { return false; }