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
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
}
}
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());
}
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?