This discussion is archived
8 Replies Latest reply: Feb 1, 2013 10:48 AM by ascuccimarra RSS

TextArea - Is it possible to get the number of lines?

ascuccimarra Newbie
Currently Being Moderated
I was wondering if there's any way to know how many lines of text a textarea has. And also, if it'd be possible to listen for number of lines changes. I'm trying to develop a component which displays just one line at first, and then starts to grow as necessary, as the number of written lines increase.
Let me know if it's not clear enough.

Thanks in advance.
  • 1. Re: TextArea - Is it possible to get the number of lines?
    James_D Guru
    Currently Being Moderated
    The quick and dirty way would be
    textArea.getText().split("\n").length
    and of course you could create a Binding to which you could listen for changes with
    IntegerBinding lineCount = new IntegerBinding() {
     { super.bind(textArea.textProperty()); }
     @Override
     public int computeValue() {
      return textArea.getText().split("\n").length ;
     }
    };
    Performance could get a bit ugly if there were lots of text. For that case you can create a subclass of TextArea, create a lineCount property, and override the insertText(...) and various replace...(...) and delete...(...) methods to keep it holding the correct value. You may also need to consider platform-dependent line separators....
  • 2. Re: TextArea - Is it possible to get the number of lines?
    805536 Journeyer
    Currently Being Moderated
    As James_D said, splitting it is the fast way to code it, but if you need efficiency, it would probably be better to loop over the string's characters rather than creating entire separate strings:
    int lineCount = 0;
    
    for (int i = 0; i < theString.length(); ++i) {
        if (theString.charAt(i) == '\n') {
            ++lineCount;
        }
    }
    You may also need to consider platform-dependent line separators
    >

    Java Swing at least "uses the '\n' character internally to represent newlines" and I imagine that it is the same for JavaFX. I don't think it is going to matter unless you're dealing with file I/O.
  • 3. Re: TextArea - Is it possible to get the number of lines?
    James_D Guru
    Currently Being Moderated
    aidreamer wrote:
    As James_D said, splitting it is the fast way to code it, but if you need efficiency, it would probably be better to loop over the string's characters rather than creating entire separate strings:
    int lineCount = 0;
    
    for (int i = 0; i < theString.length(); ++i) {
    if (theString.charAt(i) == '\n') {
    ++lineCount;
    }
    }
    Memory considerations shouldn't really be an issue; the amount of memory required for the array of strings you create using String.split(...) will be very slightly larger than the memory being used for the text. The additional memory (basically doubling what's used to store the text) will be immediately eligible for garbage collection. Modern GC implementations are extremely efficient for reclaiming memory from objects that immediately go out of scope like this.

    The bigger concern is CPU time. If you bind to the textProperty, then each character that's typed will invoke the loop or the call to String.split(). The loop and String.split are probably equivalent in terms of CPU time (though the authors of String.split(...) are very clever people who may know really efficient ways to do this...); that is they are linear with respect to the length of text that is there. Thus the total time spent counting line breaks is O(n^2) where n is the number of characters typed. Overriding the methods which manipulate the text in TextArea would run in linear time.
    >
    You may also need to consider platform-dependent line separators
    >

    Java Swing at least "uses the '\n' character internally to represent newlines" and I imagine that it is the same for JavaFX. I don't think it is going to matter unless you're dealing with file I/O.
    That sounds correct.
  • 4. Re: TextArea - Is it possible to get the number of lines?
    ascuccimarra Newbie
    Currently Being Moderated
    Hey guys, thanks for the response. I was doing something similar to that (I had added a key pressed event filter, and whenever event.getCode() == KeyCode.ENTER I increaed the height. The thing is, I have the textarea setWrap(true), so when you're typing and reach the end, the text jumps to the next line. Problem is, that doesn't count as a "\n". It seems there's no way of knowing exactly how many lines of text the textarea is actuallly rendering, right?
  • 5. Re: TextArea - Is it possible to get the number of lines?
    805536 Journeyer
    Currently Being Moderated
    I suppose you could try taking the width of the TextArea and use FontMetrics to calculate when each line would begin, but yeah, I don't think there is a good way to get the number of displayed lines. If you think this feature is important, you could try requesting it from http://javafx-jira.kenai.com
  • 6. Re: TextArea - Is it possible to get the number of lines?
    ascuccimarra Newbie
    Currently Being Moderated
    Yeah I'll do that. I wanted to check if there was any, not so ugly, workaround that I hadn't think of.
    Thanks a lot.
  • 7. Re: TextArea - Is it possible to get the number of lines?
    SaiPradeepDandem Explorer
    Currently Being Moderated
    Hi,
    May be what i am going to propose you is not the answer for your quesion. :P, but i have the same requirement and I achieved it in the following way. :)

    I wrapped by TextArea with a StackPane, and before adding the text area into the StackPane i added a Label, whose wrapText is set to true, and binded the prefWidth and text property with TextArea , as below.
    this.label =new Label();
    this.label.setWrapText(true);
    this.label.prefWidthProperty().bind(this.textArea.widthProperty());
    this.label.textProperty().bind(this.textArea.textProperty());
    That's it !!! It worked for me ;)

    Below is the complete code of this new customised control ;)
    ScrollFreeTextArea textArea= new ScrollFreeTextArea();
    And the complete class :
    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);
              }
         }
         
    }
    I hope you got the basic trick that I am doing here. I hope this can help you.

    Happy Coding :)

    Regards,
    Sai Pradeep Dandem.
  • 8. Re: TextArea - Is it possible to get the number of lines?
    ascuccimarra Newbie
    Currently Being Moderated
    Thanks a lot Sai. That does the trick indeed. I've filed the RFE anyway, cause clearly there should be an easier way. Appreciate your help.

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points