FULL PRODUCT VERSION :
java version "10" 2018-03-20
Java(TM) SE Runtime Environment 18.3 (build 10+46)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10+46, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
macOS High Sierra
A DESCRIPTION OF THE PROBLEM :
Key operation behavior related to selection of the first and last line of TextArea is incorrect.
It is different from the behavior of TextField and behavior of many other common text components.
It makes you feel stressed with text input which is one of the most important input operations. This greatly affects the impression of JavaFX quality.
The following key operation behavior is incorrect
on the first line
* Shift + UP
* UP
on the last line
* Shift + DOWN
* DOWN
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Shift + UP test
1) Execute the test
2) Enter Shift + UP on the first line (after 1 col)
UP test
1) Execute the test
2) Enter UP on the first line (after 1 col)
Test Shift + DOWN
1) Execute the test
2) Enter Shift + DOWN on the last line (after 1 col)
Test DOWN
1) Execute the test
2) Enter DOWN on the last line (after 1 col)
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
It is the same as the result of operation with TextFIeld.
ACTUAL -
Operation ignored
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.Event;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TextAreaShiftUpTest extends Application{
@Override
public void start(Stage stage) throws Exception {
TextArea textArea = new TextArea("shift+arrow up\n****************\nshift+arrow down");
// installFix(textArea);
Label caretPos = new Label();
textArea.caretPositionProperty().addListener((ob,oldBalue,newValue)->{
int pos = newValue.intValue();
String subtext = textArea.getText().substring(0, pos);
long line = subtext.codePoints().filter((c)->c=='\n').count() + 1;
caretPos.setText("line:"+line);
});
VBox root = new VBox(8, textArea, caretPos, new TextField("123456789"));
Scene scene = new Scene(root, 400, 400);
stage.setScene(scene);
stage.show();
}
private static void installFix(final TextArea textArea) {
textArea.addEventFilter(KeyEvent.ANY, (e) -> {
if (e.getEventType() != KeyEvent.KEY_PRESSED) {
return;
}
final boolean isCtrlDown = IS_MAC_OS ? e.isMetaDown() : e.isControlDown();
// FIX: UP
if (!e.isShiftDown() && !isCtrlDown && e.getCode() == KeyCode.UP) {
final int line = possitionToLine(textArea, textArea.getCaretPosition());
if (line == 1) {
textArea.home();
e.consume();
}
}
// FIX: DOWN
if (!e.isShiftDown() && !isCtrlDown && e.getCode() == KeyCode.DOWN) {
final int line = possitionToLine(textArea, textArea.getCaretPosition());
final int lastLine = countLines(textArea);
if (lastLine == line) {
textArea.end();
e.consume();
}
}
// FIX: SHift+UP
if (e.isShiftDown() && !isCtrlDown && e.getCode() == KeyCode.UP) {
final int line = possitionToLine(textArea, textArea.getCaretPosition());
if (line == 1) {
e.consume();
Platform.runLater(() -> {
final KeyEvent event = new KeyEvent(KeyEvent.KEY_PRESSED, null, null, KeyCode.HOME, true, false,
false, false);
Event.fireEvent(e.getTarget(), event);
final KeyEvent event2 = new KeyEvent(KeyEvent.KEY_RELEASED, null, null, KeyCode.HOME, true,
false, false, false);
Event.fireEvent(e.getTarget(), event2);
});
}
}
// FIX: SHift+DOWN
if (e.isShiftDown() && !isCtrlDown && e.getCode() == KeyCode.DOWN) {
final int line = possitionToLine(textArea, textArea.getCaretPosition());
final int lastLine = countLines(textArea);
if (lastLine == line) {
e.consume();
Platform.runLater(() -> {
final KeyEvent event = new KeyEvent(KeyEvent.KEY_PRESSED, null, null, KeyCode.END, true, false,
false, false);
Event.fireEvent(e.getTarget(), event);
final KeyEvent event2 = new KeyEvent(KeyEvent.KEY_RELEASED, null, null, KeyCode.END, true,
false, false, false);
Event.fireEvent(e.getTarget(), event2);
});
}
}
});
}
private static int possitionToLine(final TextArea textArea, final int pos) {
final String subtext = textArea.getText().substring(0, pos);
final long line = subtext.codePoints().filter((c) -> c == '\n').count() + 1;
return (int) line;
}
private static int countLines(final TextArea textArea) {
return (int) textArea.getText().codePoints().filter((c) -> c == '\n').count() + 1;
}
public static void main(String[] args) {
Application.launch(args);
}
private static final boolean IS_MAC_OS = System.getProperty("os.name").contains("Mac OS");
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Enable installFix included in test
java version "10" 2018-03-20
Java(TM) SE Runtime Environment 18.3 (build 10+46)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10+46, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
macOS High Sierra
A DESCRIPTION OF THE PROBLEM :
Key operation behavior related to selection of the first and last line of TextArea is incorrect.
It is different from the behavior of TextField and behavior of many other common text components.
It makes you feel stressed with text input which is one of the most important input operations. This greatly affects the impression of JavaFX quality.
The following key operation behavior is incorrect
on the first line
* Shift + UP
* UP
on the last line
* Shift + DOWN
* DOWN
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Shift + UP test
1) Execute the test
2) Enter Shift + UP on the first line (after 1 col)
UP test
1) Execute the test
2) Enter UP on the first line (after 1 col)
Test Shift + DOWN
1) Execute the test
2) Enter Shift + DOWN on the last line (after 1 col)
Test DOWN
1) Execute the test
2) Enter DOWN on the last line (after 1 col)
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
It is the same as the result of operation with TextFIeld.
ACTUAL -
Operation ignored
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.Event;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TextAreaShiftUpTest extends Application{
@Override
public void start(Stage stage) throws Exception {
TextArea textArea = new TextArea("shift+arrow up\n****************\nshift+arrow down");
// installFix(textArea);
Label caretPos = new Label();
textArea.caretPositionProperty().addListener((ob,oldBalue,newValue)->{
int pos = newValue.intValue();
String subtext = textArea.getText().substring(0, pos);
long line = subtext.codePoints().filter((c)->c=='\n').count() + 1;
caretPos.setText("line:"+line);
});
VBox root = new VBox(8, textArea, caretPos, new TextField("123456789"));
Scene scene = new Scene(root, 400, 400);
stage.setScene(scene);
stage.show();
}
private static void installFix(final TextArea textArea) {
textArea.addEventFilter(KeyEvent.ANY, (e) -> {
if (e.getEventType() != KeyEvent.KEY_PRESSED) {
return;
}
final boolean isCtrlDown = IS_MAC_OS ? e.isMetaDown() : e.isControlDown();
// FIX: UP
if (!e.isShiftDown() && !isCtrlDown && e.getCode() == KeyCode.UP) {
final int line = possitionToLine(textArea, textArea.getCaretPosition());
if (line == 1) {
textArea.home();
e.consume();
}
}
// FIX: DOWN
if (!e.isShiftDown() && !isCtrlDown && e.getCode() == KeyCode.DOWN) {
final int line = possitionToLine(textArea, textArea.getCaretPosition());
final int lastLine = countLines(textArea);
if (lastLine == line) {
textArea.end();
e.consume();
}
}
// FIX: SHift+UP
if (e.isShiftDown() && !isCtrlDown && e.getCode() == KeyCode.UP) {
final int line = possitionToLine(textArea, textArea.getCaretPosition());
if (line == 1) {
e.consume();
Platform.runLater(() -> {
final KeyEvent event = new KeyEvent(KeyEvent.KEY_PRESSED, null, null, KeyCode.HOME, true, false,
false, false);
Event.fireEvent(e.getTarget(), event);
final KeyEvent event2 = new KeyEvent(KeyEvent.KEY_RELEASED, null, null, KeyCode.HOME, true,
false, false, false);
Event.fireEvent(e.getTarget(), event2);
});
}
}
// FIX: SHift+DOWN
if (e.isShiftDown() && !isCtrlDown && e.getCode() == KeyCode.DOWN) {
final int line = possitionToLine(textArea, textArea.getCaretPosition());
final int lastLine = countLines(textArea);
if (lastLine == line) {
e.consume();
Platform.runLater(() -> {
final KeyEvent event = new KeyEvent(KeyEvent.KEY_PRESSED, null, null, KeyCode.END, true, false,
false, false);
Event.fireEvent(e.getTarget(), event);
final KeyEvent event2 = new KeyEvent(KeyEvent.KEY_RELEASED, null, null, KeyCode.END, true,
false, false, false);
Event.fireEvent(e.getTarget(), event2);
});
}
}
});
}
private static int possitionToLine(final TextArea textArea, final int pos) {
final String subtext = textArea.getText().substring(0, pos);
final long line = subtext.codePoints().filter((c) -> c == '\n').count() + 1;
return (int) line;
}
private static int countLines(final TextArea textArea) {
return (int) textArea.getText().codePoints().filter((c) -> c == '\n').count() + 1;
}
public static void main(String[] args) {
Application.launch(args);
}
private static final boolean IS_MAC_OS = System.getProperty("os.name").contains("Mac OS");
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Enable installFix included in test