4

If text wrapping was set to off, then this would be a trivial matter of course, because lines can be easily counted by counting the number of newline characters. Unfortunately, when text wrapping is on, there does not seem to be any obvious and non-messy way to do this, other than actually putting in newline characters at the end of every row when initializing the box.

Up until now, this is how I solved the problem, however it is highly undesirable since, in many cases, it inserts a newline into the middle of a word, and of course, this solution is convoluted and messy. Does anyone have any suggestions? If I were to wrap my text area inside a textflow container, would there be a way to get around this?

CodingAddict
  • 171
  • 3
  • 6
  • I've done a bit of digging on this... the actual layout is done in the "skin", i.e. TextAreaSkin... which you can get from downcasting from area.getSkin()... It is possible you can get the number of lines from there, not sure how yet...... messy I know... and subject to breakage as TextAreaSkin is sun.com private stuff... – Adam Nov 12 '15 at 07:35
  • Thank you very much for the lead. I will do some digging myself. If you find out anything else, please let me know. Do you have any recommendations/good resources about where I can read more about this "sun.com private stuff" such as textareaskin? This is probably exactly what I'll need to do to get this to work... – CodingAddict Nov 12 '15 at 07:58
  • A couple of other comments: in [Java 9](https://jdk9.java.net/download/), `TextAreaSkin` will be public API. Note the package name will change however. So if you can, you might want to start coding against that version, using the EA release. There's a `getCharacterBounds(int offset)` method, which at least in theory you could use to figure out how many rows there are (count the number of distinct y-values of the bounds, for some suitable definition of "distinct"). Or you could subclass that and define your own skin class which tracks the number of rows. Using Java 9 might help either way. – James_D Nov 12 '15 at 20:25
  • And if you can use third-party libraries, the skin classes in [RichTextFX](https://github.com/TomasMikula/RichTextFX) are also public. – James_D Nov 12 '15 at 20:27
  • Could you tell me more about how to use RichTextFX with Scene Builder 2.0? I went to Import-->FXML, but it doesn't seem to recognize or add the Rich Text controls... – CodingAddict Nov 16 '15 at 04:43

2 Answers2

1

I faced the same problem. After some digging I found this thread which has the answer you are looking for.

It uses a Label to hold the text of the TextArea and updates its height as the Label's height is changed. Here is a complete demo:

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.GroupBuilder;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.StackPaneBuilder;

public class ScrollFreeTextArea extends StackPane{

     private Label label;
     private StackPane lblContainer ;
     private TextArea textArea;

     private Character NEW_LINE_CHAR = new Character((char)10);
     private final double NEW_LINE_HEIGHT = 18D;
     private final double TOP_PADDING = 3D;
     private final double BOTTOM_PADDING = 6D;

     public ScrollFreeTextArea(){
          super();
          configure();
     }

     public ScrollFreeTextArea(String text){
          super();
          configure();
          textArea.setText(text);
     }

     private void configure(){
          setAlignment(Pos.TOP_LEFT);

          this.textArea =new TextArea();
          this.textArea.setWrapText(true);
          this.textArea.getStyleClass().add("scroll-free-text-area");


          this.label =new Label();
          this.label.setWrapText(true);
          this.label.prefWidthProperty().bind(this.textArea.widthProperty());
          this.label.textProperty().bind(this.textArea.textProperty());

          this.lblContainer = StackPaneBuilder.create()
                                                    .alignment(Pos.TOP_LEFT)
                                                    .padding(new Insets(4,7,7,7))
                                                    .children(label)
                                                    .build();
          // Binding the container width to the TextArea width.
          lblContainer.maxWidthProperty().bind(textArea.widthProperty());

          textArea.textProperty().addListener(new ChangeListener<String>() {
               @Override
               public void changed(ObservableValue<? extends String> paramObservableValue,     String paramT1, String value) {
                    layoutForNewLine(textArea.getText());
               }
          });

          label.heightProperty().addListener(new ChangeListener<Number>() {
               @Override
               public void changed(ObservableValue<? extends Number> paramObservableValue,     Number paramT1, Number paramT2) {
                    layoutForNewLine(textArea.getText());
               }
          });

          getChildren().addAll(GroupBuilder.create().children(lblContainer).build(),textArea);
     }

     private void layoutForNewLine(String text){
          if(text!=null && text.length()>0 && 
                         ((Character)text.charAt(text.length()-1)).equals(NEW_LINE_CHAR)){ 
               textArea.setPrefHeight(label.getHeight() + NEW_LINE_HEIGHT + TOP_PADDING + BOTTOM_PADDING);
               textArea.setMinHeight(label.getHeight() + NEW_LINE_HEIGHT + TOP_PADDING + BOTTOM_PADDING);
          }else{
               textArea.setPrefHeight(label.getHeight() + TOP_PADDING + BOTTOM_PADDING);
               textArea.setMinHeight(label.getHeight() + TOP_PADDING + BOTTOM_PADDING);
          }
     }

}
Dipu
  • 6,999
  • 4
  • 31
  • 48
0

From your TextArea

TextArea textArea = new TextArea();

Take the text

String toCount = textArea.getText();

Split it into lines

String[] lineArray = toCount.split("\n");

Index 0 to lineArray.length -1 is your lines - where lineArray.length -1 is your total.

How to split a string in Java

How you insert the line count number if you want them to be visable is another matter.

Lealo
  • 331
  • 1
  • 3
  • 11