nav
nav

Reputation: 145

JavaFX word-wrap on a Treeview, TreeItem/TreeCell

I want to have a collapsible list so I'm using a TreeView, but longer strings are giving horizontal scrollbars instead of word wrapping. I've tried using the CSS property -fx-wrap-text on the .tree-cell class but unfortunately nothing seems to happen.

Are TreeCells not meant to span more than one line? What alternative is there if that's the case?

Here's an image of what it looks like now:

enter image description here

test.css

.tree-cell {
        -fx-wrap-text: true;
        -fx-text-fill: blue;
    }

subjects.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.net.URL?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication1.FXMLDocumentController">
    <Button fx:id="button" layoutX="182.0" layoutY="14.0" onAction="#handleButtonAction" text="Click Me!" />
    <TextField fx:id="filterSubject" layoutX="21.0" layoutY="14.0" />
    <TreeView fx:id="subjectList" editable="true" layoutX="14.0" layoutY="61.0" prefHeight="200.0" prefWidth="365.0" />
    <stylesheets>
            <URL value="@test.css" />
    </stylesheets>  
</AnchorPane>

subjects.txt - just pairs of text where the first is subject, and second is description.

1
Somewhat long string that I want to wordwrap.
2
Somewhat long string that I want to wordwrap.
3
Somewhat long string that I want to wordwrap.

FXMLDocumentController.java

package javafxapplication1;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TreeView;
import javafx.scene.control.TreeItem;
import java.io.*;

public class FXMLDocumentController implements Initializable {


    @FXML
    private TreeView<String> subjectList;

    @FXML
    private void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
    }

    private void getSubjects(){
        TreeItem<String> subjectRoot = new TreeItem<String> ("Subject");
        subjectRoot.setExpanded(true);
        try {
            BufferedReader fileReader = new BufferedReader(
                    new FileReader("subjects.txt")
            );
            String subject = null;
            String description = null;
            while((subject = fileReader.readLine()) != null) {
                description = fileReader.readLine();
                TreeItem<String> subjectTree = new TreeItem<String> (subject);
                TreeItem<String> descriptionItem = new TreeItem<String> (description);
                subjectTree.getChildren().add(descriptionItem);
                subjectRoot.getChildren().add(subjectTree);

            }  
        } catch(FileNotFoundException e) {
            System.out.println("File not found");
        } catch(IOException e) {
            e.printStackTrace();
        }
        subjectList.setRoot(subjectRoot);
    }
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        getSubjects();
    }      
}

JavaFXApplication1.Java

package javafxapplication1;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;


public class JavaFXApplication1 extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

        Scene scene = new Scene(root);

        stage.setScene(scene);
        stage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }

}

Upvotes: 3

Views: 1890

Answers (2)

Nick B
Nick B

Reputation: 167

You can do this by setting the tree cell's prefWidth value in a treecell cellfactory (along with the treecell's wraptext value to true), which will then stop the cell from expanding horizontally with a scrollPane.

Upvotes: 0

DVarga
DVarga

Reputation: 21799

Actually the CSS class is working as you expect.

The problem is: The TreeView contains TreeCells. These TreeCells will wrap its text, in case of they will have not enough vertical space to grow vertically. As the TreeView has a built in "ScrollPane" which ones view-port will grow vertically, it will provide enouth space for the TreeCell instances in any case to grow, therefore the wrapping will never be enabled.

To avoid this you could set the cell factory to generate the TreeCell instances manually. And then you can bind the prefWidthProperty of these elements to the widthProperty of the TreeView.

Example

public class WrappedTreeView extends Application {

    @Override
    public void start(Stage stage) {
        final Scene scene = new Scene(new Group(), 200, 400);
        Group sceneRoot = (Group) scene.getRoot();

        scene.getStylesheets().add(getClass().getResource("application.css").toString());

        TreeItem<String> root = new TreeItem<>("Root");
        root.setExpanded(true);

        TreeItem<String> childNode1 = new TreeItem<>("I am a very long node - the first one -, my text must be wrapped! If it is not wrapped, it's a problem!");
        TreeItem<String> childNode2 = new TreeItem<>("I am a very long node - the second one -, my text must be wrapped! If it is not wrapped, it's a problem!");
        TreeItem<String> childNode3 = new TreeItem<>("I am a very long node - the third one -, my text must be wrapped! If it is not wrapped, it's a problem!");

        root.getChildren().addAll(childNode1, childNode2, childNode3);
        childNode2.getChildren().add(new TreeItem<>("And I am a very long embedded node, so my text must be wrapped!"));

        TreeView<String> treeView = new TreeView<>(root);
        treeView.setCellFactory(item -> {
            TreeCell<String> treeCell = new TreeCell<String>() {
                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);
                    if (item != null && !empty)
                        setText(item);
                    else
                        setText("");
                }
            };

            treeCell.prefWidthProperty().bind(treeView.widthProperty().subtract(5.0));
            return treeCell;
        });

        treeView.setMaxWidth(200);

        sceneRoot.getChildren().add(treeView);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

The content of application.css

.tree-cell { 
    -fx-wrap-text: true;
    -fx-text-fill: blue;
}

And the generated TreeView:

enter image description here

Upvotes: 3

Related Questions