Search code examples
user-interfacebuttonjavafxlabelfxml

How to find the end point of a paragraph (Label) in JavaFX?


Simple question, hopefully there is a simple solution. I have a Label in my JavaFX application in which I would like a button to be at the end of a paragraph. By the end of the paragraph, I mean as if it were another character. I cannot set the X and Y to specific values because the length of the paragraph is not always the same, so the place where this button should be is not always the same.

Below, the red is where I would like a button to be. Any way to programmatically find this point?

enter image description here

Thanks


Solution

  • You can use a helper method to split your long text into a List of Text nodes that represent a single row of text. Then, after adding your Text to a FlowPane, just add the Button at the end.

    Take a look at the following MCVE that demonstrates:

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.layout.FlowPane;
    import javafx.scene.layout.VBox;
    import javafx.scene.text.Text;
    import javafx.stage.Stage;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.StringTokenizer;
    
    public class TextFlowSample extends Application {
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage primaryStage) {
    
            // Simple interface
            VBox root = new VBox(5);
            root.setPadding(new Insets(10));
            root.setAlignment(Pos.CENTER);
    
            String longText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc nibh sapien, commodo in libero ac, feugiat accumsan odio. Aliquam erat volutpat. Integer accumsan sapien at elit aliquet sagittis. Praesent fermentum nec nunc ultrices mollis. Nam in odio ullamcorper, eleifend quam quis, aliquam sem. Nullam feugiat ex nec elit rutrum blandit. Etiam ut mauris magna. Proin est nunc, viverra quis tempus sed, dictum in lacus.";
    
            // Create a FlowPane to hold our Text
            FlowPane flowPane = new FlowPane();
    
            // Now, our button
            Button button = new Button("Click This!");
    
            // Let's use our custom method to get a list of Text objects to add to the FlowPane
            // Here we can set our max length and determine if we'll allow words to be broken up
            flowPane.getChildren().addAll(getTextsFromString(longText, 75, false));
            flowPane.getChildren().add(button);
    
            root.getChildren().add(flowPane);
    
            // Show the Stage
            primaryStage.setWidth(425);
            primaryStage.setHeight(300);
            primaryStage.setScene(new Scene(root));
            primaryStage.show();
        }
    
        /**
         * Helper method to convert a long String into a List of Text objects
         */
        private List<Text> getTextsFromString(String string, int maxLineLength, boolean breakWords) {
    
            List<Text> textList = new ArrayList<>();
    
            // If we are breaking up words, just cut into rows up to the max length
            if (breakWords) {
                int index = 0;
                while (index < string.length()) {
                    textList.add(new Text(string.substring(index, Math.min(index + maxLineLength, string.length()))));
                    index += maxLineLength;
                }
            } else {
    
                // First, let's insert linebreaks into the String when max length is reached. The tokenizer here
                // allows us to split the string and keep the spaces, making it easy to loop through later.
                StringTokenizer tokenizer = new StringTokenizer(string, " ", true);
                StringBuilder sb = new StringBuilder();
    
                int currentLength = 0;
                while (tokenizer.hasMoreTokens()) {
                    String word = tokenizer.nextToken();
    
                    // If the next word would exceed our max length, add the new line character
                    if (currentLength + word.length() > maxLineLength) {
                        sb.append("\n");
                        currentLength = 0;
                    }
    
                    sb.append(word);
                    currentLength += word.length();
                }
    
                // Now we can split the string using the \n as a delimiter
                List<String> rows = Arrays.asList(sb.toString().split("\n"));
                for (String row : rows) {
                    textList.add(new Text(row));
                }
    
            }
    
            return textList;
    
        }
    }
    

    The Result:

    screenshot