5

How do I disable the default expand/collapse behaviour when double clicking a TreeItem in a TreeView?

My question is exactly the same as this one: Disable TreeItem's default expand/collapse on double click JavaFX 2.2 Unfortunately the provided answers don't work anymore (if they ever did).

UPDATE 1: Unfortunately I didn't know that it is important to mention that I don't use the standard TreeItem, but a modified one. Its constructor looks like this:

public BasicTreeItem(String text) {
  super();

  _label = new Label(text);
  stateIndicator = new ImageView();
  this.setToNever(); // sets the stateIndicator mentioned above

  _hb = new HBox();
  this.setValue(_hb);
  _hb.getChildren().add(stateIndicator);
  _hb.getChildren().add(_label);
  _hb.setAlignment(Pos.CENTER_LEFT);
}

So the thing that is seen in the end is the HBox.

yoshegg
  • 69
  • 9
  • curious: why do you want to change standard (at least on win) UX? typical users are not happy about such tweaks ;) – kleopatra Sep 28 '17 at 07:58
  • My TreeItems consist of a special CheckBox with 4 different states which I want to change quickly. If I click too fast on them, the TreeItems expands/collapses, which I don't want to. But if I doubleclick on the Label, I still want the item to expand / collapse. – yoshegg Sep 28 '17 at 08:21
  • thanks for the info :) – kleopatra Sep 28 '17 at 09:40

1 Answers1

7

You can for example use the CellFactory of the TreeView to attach an EventFilter on mouse listener to the TreeCells of the tree.

Example:

public class TreeViewExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();

        TreeView tw = new TreeView();
        TreeItem rootNode = new TreeItem("Root");
        TreeItem blockOne = new TreeItem("Block1");
        TreeItem childA = new TreeItem("ChildA");
        TreeItem childB = new TreeItem("ChildB");
        blockOne.getChildren().add(childA);
        blockOne.getChildren().add(childB);
        TreeItem blockTwo = new TreeItem("Block2");
        TreeItem childC = new TreeItem("ChildC");
        TreeItem childD = new TreeItem("ChildD");
        blockTwo.getChildren().add(childC);
        blockTwo.getChildren().add(childD);
        rootNode.getChildren().add(blockOne);
        rootNode.getChildren().add(blockTwo);
        tw.setRoot(rootNode);


        tw.setCellFactory(param -> {
            TreeCell<String> treeCell = new TreeCell<String>() {
                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);
                    if (item == null || empty) {
                        setText("");
                        setGraphic(null);
                        return;
                    }

                    setText(item.toString());
                }
            };

            treeCell.addEventFilter(MouseEvent.MOUSE_PRESSED, (MouseEvent e) -> {
                if (e.getClickCount() % 2 == 0 && e.getButton().equals(MouseButton.PRIMARY))
                    e.consume();
            });
            return treeCell;
        });

        root.getChildren().add(tw);
        Scene scene = new Scene(root, 300, 250);
        primaryStage.setTitle("TreeView!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

Update for your particulat use case:

First of all, your TreeItem implementation is really strange, as it must be something like:

class BasicTreeItem extends TreeItem<HBox>

It's a bad practice to use GUI elements as model. You could implement a class that is able to store all the states that each item can have, and use that class as model for the TreeView and also as generic parameter for your BasicTreeItem implementation.

But to answer the update question: If you want to have the double-click expand behaviour when the Label is double clicked, but you dont want to have it if the ImageView is double clicked.

In this case you can remove the CellFactory completely and add the EventFilter to the ImageView (e.g. in your BasicTreeItem constructor):

stateIndicator.addEventFilter(MouseEvent.MOUSE_PRESSED, (MouseEvent e) -> {
    if (e.getClickCount() % 2 == 0 && e.getButton().equals(MouseButton.PRIMARY))
        e.consume();
});

With this fitler your TreeItem will have the standard double-click behaviour except double clicking on the ImageView.

DVarga
  • 21,311
  • 6
  • 55
  • 60
  • Thank you very much. Unfortunately I forgot to mention that I use an own version of TreeItem, which I added now to the original question. If you know how to `setCellFactory` for that, I would be more than happy :) – yoshegg Sep 28 '17 at 08:49
  • That did it :). In my next project, I will surely not use GUI elements as model anymore, but right now I've come to far to rework everything. – yoshegg Sep 28 '17 at 09:34
  • Just to make it clear, what did you mean by "It's a bad practice to use GUI elements as model."? Is it because of the use of `HBox`` (which is actually just used to show the relevant information in the `TreeView` or because I store data within the `TreeItem`? Basically I'm wondering if `TreeItem` would be a model where I can store data (by extending the `TreeItem`class) or even take more of them to store a tree-like structure of data. – yoshegg Oct 17 '17 at 19:42
  • Thanks! I had wondered why my attempt wasn't working. I was filtering MOUSE_CLICKED events not MOUSE_PRESSED, – swpalmer Feb 19 '23 at 03:39