Reputation: 3235
We are trying to count the number of rows in a TextArea
Here are the TextArea properties PrefWidth 600 and PrefHeight 620 with MaxHeight 620
Wrap Text is set to true. We are using JavaFX 8 with Scene Builder
We have a textPropertyListiner that will fire an Alert when the TextArea.getLength is greater than some value
The issue with this method is it does not consider the user entering a carriage return \n
So we implemented this code to capture the \n
String toCount = txaDiaryEntry.getText();
String [] lineArray = toCount.split("\n");
int LA = lineArray.length - 1;
if(LA == 0){
rc = rc - 1;
System.out.println("###### LA "+LA+" RC "+rc);
}
The if test is always ZERO so this runs every time the user types anything after one carriage return is entered
This code is inside the textPropertyListiner
When the entered text does a line wrap no \n is created
We have looked at numerous old post and tried a few examples with no results
The question is how to count rows in a TextArea that has carriage returns and line wrap is true?
As I am testing I notice some of the issue with the posted code is that the LA value continues to increase. Because we seldom work with Array My guess is that the Array needs to be cleared when the value reaches 1
So if anyone would care to explain how to accomplish that with this String[] array we will give that a test
We have EDITED the question code to reflect a working example of counting BOTH line wraps and when the user presses the ENTER key. While this works we might add that using Row Count to prevent further text entry is not as favorable as counting the number of characters in the TextArea.
@Override
public void initialize(URL url, ResourceBundle rb) {
txtTitle.setStyle("-fx-text-fill: black;");
getDate();
if(doEDIT.equals("TRUE")){
btnEdit.setVisible(true);
btnSave.setVisible(false);
try {
ReadChildTable();
} catch (SQLException ex) {
Logger.getLogger(EnterController.class.getName()).log(Level.SEVERE, null, ex);
}
}
try {
ReadParent();
} catch (SQLException | IOException ex) {
Logger.getLogger(EnterController.class.getName()).log(Level.SEVERE, null, ex);
}
txaDiaryEntry.textProperty().addListener((observable, oldValue, newValue) -> {
EC = txaDiaryEntry.getLength();
tEC = tEC + 1;
// this counts line wraps every 62 char
if(tEC == 62){
RC = RC - 1;
tEC = 0;
}
// This counts ENTER key presses
String toCount = txaDiaryEntry.getText();
String [] lineArray = toCount.split("\n");
LA = lineArray.length - 1;
if(LA == tLA){
tLA = LA + 1;
RC = RC - 1;
}else if(tLA < LA){
tLA = LA + 1;
RC = RC - (LA - 1);
}else{
}
// This test counter
int minus = EC+(LA * 40);
int val = 1200 - minus ;
txtCR.setText(String.valueOf(val));
uEC = uEC - val;
if(LA == 0){
uEC = 1200;
}else{
uEC = 960;// 880
}
if(EC > uEC){
//if(RC == 0){
alertTYPE = "4";
//RC = RC + 1;
try {
customAlert();
} catch (IOException ex) {
Logger.getLogger(EnterController.class.getName()).log(Level.SEVERE, null, ex);
}
txaDiaryEntry.requestFocus();
}
});
}
Please see comments in the code as this method manages other tasks.
Upvotes: 0
Views: 1191
Reputation: 51524
As already noted by @Slaw in one of his comments, there is no public API to access the line count in a textArea (emphasis by me). On the other hand, there is internal API that provides what we need - if we are daring enough and allowed to work with internals.
Looking at public api and digging into the implementation details of TextAreaSkin, it turns out that (currently, up to fx13 and probably later)
textArea.getParagraphs()
seems to return the charSequences that are separated by a hard linebreakgetLines()
which returns an array of textLines separated by either soft or hard linebreaks, for counting we are only interested in its length.Below is a quick example that demonstrates how to make use of these internals. Basically, it looks up the area's text node (available after the skin is attached), reflectively accesses its textLayout and query the length of the lines array.
Note that this is for fx9+ (probabbly not much changed against fx8 except for pulling the skins into public scope, didn't check, though). To allow access to internals, we need to tweak module access restrictions at both compiletime and runtime.
Compiletime:
--add-exports javafx.graphics/com.sun.javafx.scene.text=ALL_UNNAMED
Runtime:
--add-opens javafx.graphics/com.sun.javafx.scene.text=ALL-UNNAMED
--add-opens javafx.graphics/javafx.scene.text=ALL-UNNAMED
The example to play with:
public class TextAreaLineCount extends Application {
String info = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
"Nam tortor felis, pulvinar in scelerisque cursus, pulvinar at ante. " +
"Nulla consequat congue lectus in sodales.";
private Parent createContent() {
TextArea area = new TextArea(info);
area.setWrapText(true);
area.appendText("\n" + info);
Button append = new Button("append paragraph");
append.setOnAction(e -> {
area.appendText("\n " + info);
LOG.info("paragraphs: " + area.getParagraphs().size());
});
Button logLines = new Button("log lines");
logLines.setOnAction(e -> {
Text text = (Text) area.lookup(".text");
// getTextLayout is a private method in text, have to access reflectively
// this is my utility method, use your own :)
TextLayout layout = (TextLayout) FXUtils.invokeGetMethodValue(Text.class, text, "getTextLayout");
LOG.info("" + layout.getLines().length);
});
BorderPane content = new BorderPane(area);
content.setBottom(new HBox(10, append, logLines));
return content;
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TextAreaLineCount.class.getName());
}
Upvotes: 4
Reputation: 1371
Grendel this code seems to complicate the task of counting the \n by using the String[ ] array. This code posted below
// This counts ENTER key presses
String toCount = txaDiaryEntry.getText();
String [] lineArray = toCount.split("\n");
LA = lineArray.length - 1;
if(LA == tLA){
tLA = LA + 1;
RC = RC - 1;
}else if(tLA < LA){
tLA = LA + 1;
RC = RC - 1;
}else{
So rather than deal with the String [ ] array here is a little less code that just counts the occurrences of \n and as your original code did it subtracts a given number of characters
String toCount = txaDiaryEntry.getText();
S = toCount.split("\n",-1).length - 1;
RowCount = RowCount - 1;
// This test counter
int minus = EC+(S * 40);
int val = 1200 - minus ;
All the code to deal with the String [ ] array was clever but way crazy and hard to follow. Now you can play with the number 40 for perhaps a more realistic character count?
Upvotes: 2
Reputation: 9869
A Minimal, Reproducible Example is something that can be copied and run without much changes, should also have self explanatory variables.
For your reference, I am providing a demo and can you please update it as per your requirment. It is very hard to follow your code as the variables are not explanatory.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TextAreaLinesCount_Demo extends Application {
@Override
public void start(Stage stage) throws Exception {
final VBox root = new VBox();
root.setSpacing(10);
root.setPadding(new Insets(10));
final Scene sc = new Scene(root, 350, 200);
stage.setScene(sc);
stage.show();
Label lines = new Label();
Label alert = new Label();
GridPane gp = new GridPane();
gp.setHgap(10);
gp.setVgap(10);
gp.addRow(0, new Label("No of Lines:"), lines);
gp.addRow(1, new Label("Alert:"), alert);
TextArea textArea = new TextArea();
textArea.setWrapText(true);
textArea.textProperty().addListener((obs, old, text) -> {
lines.setText(text.split("\n").length + "");
validate(text, alert);
});
VBox.setVgrow(textArea, Priority.ALWAYS);
root.getChildren().addAll(gp, textArea);
textArea.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
}
private void validate(String text, Label alert) {
// Can you add your logic here.. and update the "alert" label for when to show the alert
}
}
Upvotes: 2